4.5. Разновидности связанных списков

Cписок с начальной меткой.

Удаление и вставка элемента внутри однонаправленного списка довольно просто осуществляется если известен адрес после которого надо это сделать. Поиск такого адреса так же просто осуществляется, например как в вышеприведенной процедуре readk. Однако при этом возникает проблема начального элемента, когда элемент с заданным ключом находится в первой записи списка. Эту ситуацию приходится отслеживать и обрабатывать отдельным алгоритмом. Для того, чтобы избежать таких сложностей при интенсивной работе с внутренними элементами списка бывает полезно организовать список с начальной меткой, в котором адрес начала списка sp1 остается постоянным, а все значимые информационные записи начинаются со второй ячейки. Первая информационная ячейка может использоваться для записи дополнительной информации о списке.

Создание списка с начальной меткой начинается с процедуры выделения памяти под метку и при необходимости, записи в нее информации Infm о списке. Это делается, например, с помощью метода add1:

Stekm:=create; Stecm.add1(Infm);

После создания списка с меткой работа с ним производится с помощью единообразных процедур ReadAfter, AddAfter, PoiskAfter.

Циклические связанные списки организуются таким образом, чтобы адресное поле последнего элемента списка указывало на первый элемент.

Циклические списки используются, когда нужно обходить набор элементов в бесконечном цикле. Они так же позволяют получить доступ ко всему списку начиная с любой позиции, что придает списку некоторую симметрию. Программа может работать со всеми элементами одинаково без использования меток с помощью процедурRedAfter, AddAfter, PoiskAfter. Обход циклического списка начиная с элемента имеющего адрес Spi осуществляется следующим образом:

Листинг 4.12

Procedure Tlist.Printc(spi:Psel);

begin sp:=spi;

Repeat

Write(sp^.Inf);

sp:=sp^.A;

Until sp:=spi;

end;

При работе с циклическим списком необходимо держать в памяти адрес одного из элементов.

Двухсвязные списки организуются когда требуется просматривать список как в одном так и в обратном направлениях. Мы видели, что в однонаправленном списке довольно просто удалить (вставить) новую ячейку после заданной, но довольно сложно удалить саму заданную ячейку или предыдущую ей. Эта проблема легко решается, если ввести рекурсивный тип с двумя адресными ячейками:

Type Листинг 4.13

Pseld=^seld;

seld=record

Inf:Tinf;

A1:Pseld;

A2:Pseld;

end;

Tlistd=class(Tobject)

sp1,spk,sp:Pseld;

constructor create;

procedure add1(inf:Tinf);

procedure readk(var inf:Tinf);

. . . . . . . . . .

end;

ВА1 засылается адрес предыдущего элемента, в А2 - последующего, причем в первой ячейке с адресом sp1 значение A1=Nil, в последней ячейке с адресом spk значение A2=Nil:

Добавление в начало и чтение последнего элемента:

Листинг 4.14

Procedure Tlistd.add1;

begin

if sp1=nil then

begin New(sp1); Sp1^.A1:=nil; sp1^.A2:=nil;

spk:=sp1 end;

New(sp);

sp^.A1:=Nil; sp1^.A2:=sp1;

sp^.inf:=inf; sp1^.A1:=sp;

sp1:=sp;

end;

Procedure Tlistd.readk;

begin

sp:=spk; inf:=sp^.inf;

sp^.A1^.A2:=Nil;

spk:=sp^.A1;

dispose(sp);

end;

Аналогично составляются методы добавления элемента в конец списка и удаления начального элемента

Добавление элемента после заданного в двухсвязный список:

Листинг 4.15

Procedure Tlistd.AddAfter(Inf:TInf;spi:Pseld);

begin

New(sp);

sp^.Inf:=Inf;

sp^.A1:=spi;

sp^.A2:=spi.A2;

spi^.A2:=sp;

sp^.A2^.A1:=sp;

end;

Добавление элемента перед заданным:

Procedure Tlistd.AddBeford(Inf:TInf;spi:Pseld);

begin

New(sp);

sp^.Inf:=Inf;

sp^.A2:=spi;

sp^.A1:=spi.A1;

Spi^.A1:=sp;

sp^.A1^.A2:=sp;

end;

Чтение и удаление элемента с адресом spi:

Procedure Tlistd.Read(var Inf:TInf;spi:Pseld);

begin

Inf:=spi^.Inf;

spi^.A1^.A2:=spi^.A2;

spi^.A2^.A1:=spi^.A1;

Dispose(spi);

end;