Страница 26 из 60
Списки с двойной связью
Каждый элемент в списке с двойной связью имеет указатель на следующий элемент списка и указатель на предыдущий элемент спис- ка. Рис.11 иллюстрирует характер связей в таком списке. Список, который вместо одной имеет две связи, отличается двумя основными преимуществами. Во-первых, список может читаться в обоих направ- лениях. Это не только упрощает сортировку списка, но также позво- ляет пользователям базы данных просматривать данные в обоих нап- равлениях. Во-вторых, и прямая и обратная связь позволяют прочитать список полностью и поэтому при нарушении одной из свя- зей список может быть восстановлен по другой связи. Это свойство имеет смысл использовать при отказах оборудования, приводящих к нарушению списка. -----------¬ -----------¬ ------------¬ ¦1 info ¦ ¦1 info ¦ ¦1 info ¦ +-----T----+ +-----T----+ +-----T-----+ ¦2nil ¦ ¦ ¦ ¦ ¦ ¦ ¦2 nil¦ L-----+----- L-----+----- L-----+------
Рис.11. Список с двойной связью:
1 - информационные поля; 2 - связь с нулевым значением.
Для списка с двойной связью предусматриваются три основные операции: вставка нового первого элемента, вставка нового средне- го элемента и вставка нового последнего элемента. Эти операции проиллюстрированы на рис.12. Список с двойной связью строится подобно списку с одной связью, причем в записи должно быть предусмотрено место для двух указателей. Для примера со списком почтовых корреспонденций за- пись адреса можно модифицировать следующим образом:
type str80 = string[80]; AddrPointer = ^address; address = record
1 Inserling a New First Element -------¬ -------¬ ¦2 new ¦ ¦2 new ¦ +---T--+ +---T--+ ¦ ¦ ¦ ¦ ¦ ¦ L---+--- L---+--- 4becomes
-------¬ -------¬ -------¬ -------¬ -------¬ -------¬ ¦5 info¦ ¦5 info¦ ¦5 info¦ ¦5 info¦ ¦5 info¦ ¦5 info¦ +---T--+ +--T---+ +--T---+ +--T---+ +---T--+ +--T---+ 3¦nil¦ ¦ ¦ ¦ ¦ ¦ ¦nil¦3 ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦nil¦3 L---+--- L--+---- L--+---- L--+---- L---+--- L--+---- 6 Inserling a New Middle Element -------¬ -------¬ ¦2 new ¦ ¦2 new ¦ +--T---+ +---T--+ ¦ ¦ ¦ ¦ ¦ ¦ L--+---- L---+--- 4becomes -------¬ -------¬ -------¬ -------¬ -------¬ -------¬ ¦5 info¦ ¦5 info¦ ¦5 info¦ ¦5 info¦ ¦5 info¦ ¦5 info¦ +---T--+ +--T---+ +--T---+ +---T--+ +---T--+ +--T---+ 3¦nil¦ ¦ ¦ ¦ ¦ ¦ ¦nil¦3 3¦nil¦ ¦ ¦ ¦ ¦ ¦ ¦nil¦3 L---+--- L--+---- L--+---- L---+--- L---+--- L--+---- 7 Inserling a New Last Element -------¬ -------¬ ¦2 new ¦ ¦2 new ¦ +--T---+ +--T---+ ¦ ¦ ¦ ¦ ¦nil¦3 L--+---- L--+---- 4becomes -------¬ -------¬ -------¬ -------¬ -------¬ -------¬ ¦5 info¦ ¦5 info¦ ¦5 info¦ ¦5 info¦ ¦5 info¦ ¦5 info¦ +---T--+ +--T---+ +--T---+ +---T--+ +--T---+ +---T--+ 3¦nil¦ ¦ ¦ ¦ ¦ ¦ ¦nil¦3 3¦nil¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ L---+--- L--+---- L--+---- L---+--- L--+---- L---+---
Рис.12. Вставка элемента в список с двойной связью: 1 - вставка нового первого элемента; 2 - новый элемент; 3 - указатель с нулевым значением; 4 - левый список преобразуется в правый; 5 - информационные поля; 6 - вставка нового среднего элемента; 7 - вставка нового последнего элемента. name : string[30]; street: string[40]; city: string[20]; state: string[2]; zip: string[9]; next: AddrPointer; { указатель на следующую запись } prior: AddrPointer; { указатель на предыдущую запись } end;
DataItem = address; DataArray = array [1..100] of AddrPointer;
Ниже приводится функция, которая позволяет строить список с двойной связью для записей адресов: {добавление элементов в список с двойной связью } procedure DL_Store(i: AddrPointer); begin if last=nil then { первый элемент списка } begin last:=i; start:=i; i^.next:=nil; i^.prior:=nil; end else begin last^.next:=i; i^.next:=nil; i^.prior:=last; last:=i; end; end; { конец функции добавления в список с двойной связью }
Эта функция помещает каждый новый элемент в конец списка. Следует иметь в виду, что перед первым обращением к функции пере- менные "start" и "last" должны устанавливаться в нулевое значе- ние. В ходе построения списка с двойной связью каждый новый эле- мент может /как и для списка с одной связью/ устанавливаться не в конец, а в соответствующее место, обеспечивая упорядочность эле- ментов в списке. Ниже приводится функция, которая создает список с двойной связью, упорядоченый по возрастанию фамилий из записей адресов:
{упорядоченная установка элементов в список с двойной связью} function DSL_Store(info, start: AddrPointer; var last: AddrPointer): AddrPointer; { вставка элементов в соответствующее место с сохранением порядка } var old, top: AddrPointer; done: boolean; begin top := start; old := nil;
done := FALSE;
if start = nil then begin { первый элемент списка } info^.next := nil; last := info; info^.prior :=nil; DCL_Store := info; end else begin while (start<>nil) and (not done) do begin if start^.name < info^.name then begin old := start; start := start^.next; end else begin { вставка в середину } if old <>nil then begin old^.next := info; info^.next := start; start^.prior := info; info^.prior := old; DSL_Store := top; { сохранение начала } done := TRUE; end else begin info^.next := start;{новый первый элемент } info^.prior := nil; DSL_Store := info; done := TRUE; end; end; end; { конец цикла } if not done then begin last^.next := info; info^.next := nil; info^.prior := last; last := info; DSL_Store := top; { сохранение начала } end; end; end; { конец функции DSL_Store }
Поскольку элемент может помещаться в самое начало списка, эта функция должна выдавать указатель на начало списка, чтобы в других частях программы учесть изменения начала списка. При поис- ке конкретного элемента списка, как и для списка с одиночной связью, делается последовательный проход по цепочке связей пока не будет найден требуемый элемент. При удалении элемента из списка возникает одна из трех ситу- аций: удаление первого элемента, удаление среднего элемента и удаление последнего элемента. Рис.13 иллюстрирует изменение свя- зей для этих случаев.
1 Deleting the First Item 3becomes -------¬ -------¬ -------¬ --------¬ -------¬ -------¬ ¦2 info¦ ¦2 info¦ ¦2 info¦ 4¦2 info ¦ ¦2 info¦ ¦2 info¦ +---T--+ +--T---+ +--T---+ +---T---+ +---T--+ +--T---+ 5¦nil¦ ¦ ¦ ¦ ¦ ¦ ¦nil¦5 5¦nil¦nil¦55¦nil¦ ¦ ¦ ¦nil¦5 L---+--- L--+---- L--+---- L---+---- L---+--- L--+----
6 Deleting a Middle Item 3becomes -------¬ -------¬ -------¬ -------¬ --------¬ -------¬ ¦2 info¦ ¦2 info¦ ¦2 info¦ ¦2 info¦ ¦2 info ¦ ¦2 info¦ +---T--+ +--T---+ +--T---+ +---T--+ +---T---+ +--T---+ 5¦nil¦ ¦ ¦ ¦ ¦ ¦ ¦nil¦5 5¦nil¦ ¦5 ¦nil¦nil¦5¦ ¦nil¦5 L---+--- L--+---- L--+---- L---+--- L---+---- L--+----
7 Deleting the Last Item 3becomes -------¬ -------¬ -------¬ -------¬ -------¬ --------¬ ¦2 info¦ ¦2 info¦ ¦2 info¦ ¦2 info¦ ¦2 info¦ ¦2 info ¦ +---T--+ +--T---+ +--T---+ +---T--+ +--T---+ +---T---+ 5¦nil¦ ¦ ¦ ¦ ¦ ¦ ¦nil¦5 5¦nil¦ ¦ ¦ ¦nil¦55¦nil¦nil¦5 L---+--- L--+---- L--+---- L---+--- L--+---- L---+----
Рис.13. Удаление элемента из списка с двойной связью: 1 - удаление первого элемента; 2 - информационные поля; 3 - левый список преобразуется в правый; 4 - удаленный элемент; 5 - указа- тель с нулевым значением; 6 - удаление среднего элемента; 7 - удаление последнего элемента. Ниже приводится функция, которая выполняет удаление элемента типа "address" из списка с двойной связью: { удаление элемента из списка с двойной связью } function DL_Delete(start: AddrPointer; key: str80): AddrPointer; var temp, temp2: AddrPointer; done: boolean; begin if start^.name=key then begin { первый элемент списка} DL_Delete:=start^.next; if temp^.next <> nil then begin temp := start^.next; temp^.prior := nil; end; dispose(start); end else begin done := FALSE; temp := start^.next; temp2 := start; while (temp<>nil) and (not done) do begin if temp^.name=key then begin temp^.next:=temp^.next;
if temp^.next<>nil then temp^.next^.prior:=temp2; done:=TRUE; dispose(temp); end else begin temp2:=temp; temp:=temp^.next; end; end; DL_Delete:=start; { начало не изменяется } if not done then WriteLn('not found'); end; end; { конец функции удаления элемента из списка с двойной связью } Этой функции передается на один указатель меньше, чем для соответствующей функции при использовании списка с одной связью. /Удаленный элемент уже имеет указатель на предыдущий элемент и указатель на следующий элемент/. Поскольку первый элемент в спис- ке может измениться, функция возвращает указатель на начало спис- ка.
|