4.2. Списки на основе динамических массивов

Простейшая форма организации списка – это динамический массив данных. Данные, размещенные в массиве имеют свои номера (индексы) и это придает эффект «видимости» каждому элементу. Мы уже научились некоторым приемам работы с массивами и оценили их эффективность.

Рассмотрим как организуется работа со списками на основе динамического массива a переменного размера n.

Напишем класс для работы с таким списком:

Листинг 4.1

type

Tinf=<тип элементов>;

Tms=array[1..1] of Tinf;

Pms=^Тms; //указатель на массив

Tlist=Class(Tobject)

a,a1:pms; n,mt:word;

constructor create;

procedure Addk(с:Tinf);//добавить элемент

procedure Read1(var с:Tinf);//удалить элемент

end;

constructor Tlist.create;

begin

Inherited create;

n:=0; mt:= sizeof(Tinf); a:=Nil; end;

End;

. . . . . . . . . . .

Var turn:Tlist; с1,c2:Tinf;

begin

turn.create; //создадим новую очередь

Read(n); //введем в нее сохраненный ранее список

for i:=1 to n do

begin Read(Fl,c1); turn.Addk(c1) end;

.................//далее работаем с очередью:

turn.Addk(c1); //добавляем элементы

turn.Read1(c2); //читаем и удаляем элементы

. . . . . . . . . .

turn.Free;

При реализации метода Addk добавления нового элемента в список необходимо выделить память на 1 элемент большую, затем скопировать старый список в новый раздел памяти, добавить в нужное место массива еще 1 элемент, после чего освободить ранее выделенную память и установить указатель a на новый раздел памяти:

Листинг 4.2

procedure Tlist.Addk;

begin

GetMem(a1,(n+1)* mt); a1[n+1]:=с;//добавление элемента

if n>0 then begin

for i:=1 to n do a1[i]:=a[i];//копирование элементов

FreeMem(a,mt*n);//освобождение ранее выделенной памяти

End;

a:=a1; n:=n+1;//теперь имя опять a

end;

Аналогично реализуется метод чтения и удаления элемента:

procedure Tlist.Read1;

begin

if n>0 then begin

c:=a[1]; n:=n-1;

if n>0 then begin

GetMem(a1,n* mt);

for i:=1 to n do a1[i]:=a[i+1];//копирование элементов

end else a1:=nil;

FreeMem(a,mt*(n+1));//освобождение ранее выделенной памяти

a:=a1; //теперь имя опять a

end;

end;

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

Иногда хороший выход из этой ситуации – организация изменения размера n порциями (например по 20 элементов). Можно сделать размер порции зависимой от текущей длины массива (например 10% от n).

Процедура удаления i-го элемента тоже требует кроме выделения новой памяти и копирования еще и «схлопывания», т.е. сдвига каждого элемента на одну позицию, чтобы заполнить освободившуюся i-го позицию. Вместо удаления заданного элемента, чтобы избежать копирования, его можно пометить как неиспользуемый, поместив в ключ так называемое «мусорное» значение (например ключ=0), а при организации процедур обработки списка предусмотреть это. По мере накопления мусора в списке его надо собирать, что делается за один проход копирования.