Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Списки, стеки, очереди.doc
Скачиваний:
2
Добавлен:
01.05.2025
Размер:
411 Кб
Скачать
☆

9.1.3. Двусвязный список

Линейные односвязные списки, содержащие только один указатель — на следующий элемент, позволяют двигаться лишь в одном направлении — от начала списка к его концу. Можно построить список, каждый элемент которого содержит два указателя: на следующий элемент и на предыдущий. В этом случае описание типов и переменных будет таким:

type TInfo = integer;

PNode = ^TNode;

TNode = record

Info: TInfo;

Next, // указатель на следующий элемент

Pred: PNode; // указатель на предыдущий элемент

end;

Движение по такому списку может происходить в двух направлениях: от начала к концу — с использованием указателя Next, и от конца к началу, используя указатель Pred. Пример двусвязного списка показан на рис. 9.7.

Рис. 9.7. Двусвязный список

Очевидно, что значение поля Next последнего элемента и поля Pred первого элемента линейного двусвязного списка равны Nil.

Включение нового элемента в двусвязный список и удаление элемента происходит и проще, и сложнее, чем в односвязный. Проще — потому что элемент двусвязного списка содержит указатели и на следующий, и на предыдущий элементы, и нет нужды, как при вставке в односвязный список, либо обменивать информационные поля местами, либо тащить, при проходе по списку, дополнительный указатель на предыдущий элемент. Сложнее — потому что требуется переприсвоить и значения указателей Next, и значения указателей Pred. На рис. 9.8 показана вставка элемента в двусвязный список. Прежние значения указателей показаны светло-серым цветом.

Рис. 9.8. Вставка элемента в двусвязный список

Для того чтобы вставить в список новый элемент после элемента p (см. рис. 9.8), необходимы следующие действия:

new(q); // создание нового элемента

with q^ do begin

Info:=34;

Next:=p^.Next;

Pred:=p;

end;

p^.Next^.Pred:=q; // меняется значение поля Pred элемента со значением 44

p^.Next:=q;

Удаление элемента p из двусвязного списка показано на рис. 9.9.

Рис. 9.9. Удаление элемента из двусвязного списка

Удаление производится следующими действиями:

p^.Pred^.Next:=p^.Next; // связать предыдущий элемент со следующим за p

p^.Next^.Pred:=p^.Pred; // связать следующий за p элемент с предыдущим

dispose(p) // удаление элемента

9.1.4. Кольцевой список

Если последний элемент линейного списка связать с первым посредством указателя Next, то получится кольцевой односвязный список, как показано на рис. 9.10.

Рис. 9.10. Односвязный кольцевой список

Для создания двусвязного кольцевого списка необходимо связать последний элемент с первым как по указателю Next, так и по указателю Pred. Двусвязный кольцевой список показан на рис. 9.11.

Рис. 9.11. Двусвязный кольцевой список

Разумеется, на одном из элементов кольцевого списка должен стоять указатель, и этот элемент условно считается первым.

В качестве примера использования двусвязного кольцевого списка решим следующую задачу. Дети стали в круг, взявшись за руки. Начав отсчет от первого, каждый i-тый выходит из круга, а круг смыкается. Кто останется?

Первая часть задачи — создание списка. Процедура Insert2 вставляет элемент в двусвязный список.

Листинг 9.8. Процедура Insert2 вставляет элемент в двусвязный список

procedure Insert2(var r: PNode; e: TInfo);

// вставка элемента в двусвязный список

var t: PNode;

begin

if r=Nil then begin // список пуст, создание первого элемента

new(r);

with r^ do begin

Info:=e;

Next:=r; Pred:=r; // ссылки замыкаем на себя

end;

end

else begin

new(t);

with t^ do begin

Info:=e; Next:=r^.Next; Pred:=r;

end;

r^.Next^.Pred:=t; r^.Next:=t;

end

end;

Вызвав эту процедуру K раз, получим список, содержащий K элементов. Указатель списка стоит на первом включенном в список элементе.

Вторая часть задачи – удаление i-того элемента, считая от первого. Алгоритм прост: отсчитывается заданное количество элементов, требуемый элемент удаляется, указатель на список переносится на следующий за удаленным элемент. Когда в списке останется только один элемент, его поля Pred и Next будут указывать на него самого. Функция CountDel реализует этот алгоритм.

Листинг 9.9. Удаление элемента

function CountDel (var p: PNode; step: integer): PNode;

var r,t: PNode;

i : integer;

begin

r:=p;

repeat

for i:=1 to step do r:=r^.Next;

t:=r^.Pred; // этот элемент будем удалять

t^.Pred^.Next:=r; r^.Pred:=t^.Pred; // замыкаем круг

dispose(t)

until r=r^.Next;

Result:=r;

end;