
- •Тема 4 Списки и их
- •Что такое список
- •Показательным примером является задача обслуживания очереди заказов на покупку товара
- •Список (list)
- •Очередь (turn)
- •Стек (stek)
- •Организация работы со списками на основе динамических массивов
- •Класс для работы со списком на основе массива
- •Работа с очередью
- •Метод Addk(c)
- •Программа Addk
- •Метод Read1(var c:Tinf)
- •Программа Read1
- •Резюме1
- •Резюме2
- •Понятие рекурсивных данных, косвенная адресация и однонаправленные списки
- •Программирование рекурсивного типа данных
- •Структура рекурсивного типа данных
- •Описываемая типом Тsel ячейка
- •однонаправленные (линейные)
- •Переход от одной записи к другой
- •Переход от одной записи к другой
- •Стек- список с одной точкой входа
- •Очередь- список с двумя точками входа
- •Класс для работы со списком (задание типа ячеек)
- •Работа с методами класса
- •Класс для работы со стеком
- •Класс для работы с очередью
- •Основные методы работы со стеком (класс TlistStk)
- •Add1- добавить в стек
- •Read1 – взять элемент из стека
- •Print – распечатать содержимое стека
- ••Не всегда очередь движется по правилам – «в конец добавили из начала взяли»
- •AddAftter – добавить элемент в стек после элемента с заданным в указателе spi
- •ReadAftter – взять и удалить из стека элемент после элемента с заданным адресом
- •AddBefore – добавить элемент в стек перед элементом с заданным адресом
- •Основные методы работы с очередью (класс TlistTrn)
- •Addk – добавить элемент в конец очереди
- •Read1 – взять первый элемент из очереди
- •Readk – взять последний элемент из очереди
- •Некоторые проблемы работы с однонаправленным списком
- •Список с меткой
- •Циклические связанные списки
- •Двухсвязные списки
- •Рекурсивный тип с двумя адресными ячейками
- •Движение по списку
- •Добавление элемента в начало
- •Добавление в начало
- •чтение последнего элемента
- •Чтение и удаление последнего
- •Добавление элемента после заданного в двухсвязный список
- •Добавление после заданного
- •Добавление элемента перед заданным
- •Чтение и удаление элемента с адресом spi
- •Контрольные вопросы
- •Задача на экзамен
- •Конец темы

Readk – взять последний элемент из очереди
• Procedure TlistTrn.Reаdk(var Inf:Tinf);
• Begin
Inf:=spk.Inf;
if sp1=spk then //если одна
•begin Dispose(sp1); sp1:=Nil; spk:=Nil end
•else begin
• |
sp:=sp1;//поиск предпоследней |
• |
while sp^.A<>Spk do sp:=sp^.A; |
• |
Dispose(spk); |
• |
spk:=sp; |
• |
spk^.A:=Nil; |
• |
end |
•end;
sp1 |
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
spk |
|
||||
|
|
|
…. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I1 |
A1 |
Ik-1 |
|
Ak-1 |
|
|
|
Ik |
nil |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
07/02/19 |
|
|
|
|
41 |
Некоторые проблемы работы с однонаправленным списком
Как мы видели, в однонаправленном списке довольно сложно взять элемент изнутри списка
Например последний элемент из очереди просто так удалить не получается, хотя имеется его адрес spk
Так же проблематично вставить элемент внутрь очереди перед заданным
Хотя довольно эффективно работают процедуры удаления и вставки элемента после элемента с заданным адресом ReadAftter, AddAftter.
Однако при их регулярном использовании имеется проблема первого элемента – его надо обрабатывать другой процедурой, а значит каждый раз требуется лишняя проверка
Чтобы избавиться от нее, часто используют Список с меткой
07/02/19 |
42 |

Список с меткой
sp1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
…. |
|
|
|
|
|
spk |
|
|
||||
|
|
|
I |
A |
|
|
I1 |
A1 |
|
|
|
Ik-1 |
Ak-1 |
|
|
|
Ik |
nil |
|
|
|
|
|
|
|
|
|
В таком списке от первой ячейки используется только ее адресная часть sp1.A,
в информационной же части могут быть при необходимости размещены данные об этом списке, при этом метка создается методом Add1
Stekm:=create; |
Stecm.add1(Infm); . |
При работе со стеком sp1 остается неизменным
После создания списка с меткой работа с ним производится с помощью единообразных процедур ReadAfter, AddAfter, PoiskAfter.
07/02/19 |
43 |

Циклические связанные списки
sp1 |
|
|
|
|
|
|
|
…. |
|
|
|
|
|
|
|
|
|
|
I1 |
A1 |
|
|
|
Ik-1 |
Ak-1 |
|
|
Ik |
sp1 |
|
|
||||||
|
|
|
|
|
|
|
||||||||||||
|
|
|
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Циклические списки используются, когда нужно обходить набор элементов в бесконечном цикле.
Они так же позволяют получить доступ к списку начиная с любой позиции, что придает списку некоторую симметрию.
Программа может работать со всеми элементами
одинаково без использования меток с помощью процедур RedAfter, AddAfter, PoiskAfter.
07/02/19 |
44 |
|
Обход циклического списка |
• Procedure TlistStk.Printc(sp1:Psel); |
|
• |
begin sp:=sp1; |
• |
Repeat |
• |
Write(sp^.Inf); |
• |
sp:=sp^.A; |
• |
Until sp:=sp1; |
• |
end; |
•
При работе с циклическим списком необходимо держать в памяти адрес одного из элементов sp1.
07/02/19 |
45 |

Двухсвязные списки
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
spk |
sp1 |
|
|
|
I |
|
|
|
A1 |
A2 |
|
|
|
I |
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nil |
|
А2 |
|
|
|
I |
|
|
|
A1 |
|
nil |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
организуются когда требуется просматривать список как в одном так и в обратном направлениях.
Мы видели, что в однонаправленном списке довольно просто удалить (вставить) новую ячейку после заданной, но довольно сложно удалить саму заданную ячейку или предыдущую к ней.
Эта проблема легко решается, если ввести рекурсивный тип с двумя адресными ячейками
07/02/19 |
46 |
Рекурсивный тип с двумя адресными ячейками
•Type
•Tseld=^seld;
•seld=record
• |
Inf:Tinf; |
• |
A1:Tseld; |
• |
A2:Tseld; |
•end;
•Tlistd=class(Tobject)
•sp1,spk,sp:Tseld;
•constructor create;
•procedure add1(inf:Tinf);
•procedure readk(var inf:Tinf);
•. . . . . . . . . .
•end;
07/02/19 |
47 |

Движение по списку
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
spk |
sp1 |
|
|
|
I |
|
|
|
A1 |
A2 |
|
|
|
I |
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nil |
|
А2 |
|
|
|
I |
|
|
|
A1 |
|
nil |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
•движение вправо
•Write(sp1.A2.A2.inf);
•sp:=sp1.A2;
•движение влево
•Write(spk.A1.A1.inf);
•sp:=spk.A1;
07/02/19 |
48 |

Добавление элемента в начало
•Procedure Tlistd.add1(Inf:Tinf);
•begin
•if sp1=nil then
• |
|
|
|
begin New(sp1); |
|
|
|||||||
• |
|
|
|
|
sp1^.inf:=inf; |
|
|
||||||
• |
|
|
|
|
Sp1^.A1:=nil; |
|
|
||||||
• |
|
|
|
|
sp1^.A2:=nil; |
|
|
||||||
• |
|
|
|
|
spk:=sp1 end |
|
|
||||||
• |
|
|
else begin |
|
|
||||||||
• |
New(sp); |
|
|
|
|
|
|
|
|||||
• |
sp^.A1:=Nil; sp^.A2:=sp1; |
||||||||||||
• |
sp^.inf:=inf; sp1^.A1:=sp; |
||||||||||||
• |
sp1:=sp; end; |
|
|
||||||||||
• |
end; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sp1 |
|
|
I |
|
|
|
|
A1 |
|
A2 |
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
nil |
|
А2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I |
||||
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nil
A2
07/02/19sp
I
I |
|
|
|
spk |
|
|
|
|
|||
A1 |
|
nil |
|
|
|
|
|
|
|
49

Добавление в начало
|
|
|
|
|
|
Inf |
|
|||
|
|
|
|
|
|
|
|
|
|
Если список был пуст |
sp1 |
|
|
|
I |
|
|
|
spk |
||
|
|
|
|
|
|
|
||||
|
|
|
nil |
|
|
nil |
|
|
|
|
sp1 |
I |
A1 A2 |
I |
spk |
|
nil А2 |
I |
A1 |
nil |
A1 |
A2 |
|
|
|
sp |
I |
|
|
|
nil Inf
07/02/19 |
50 |