Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по курсу.docx
Скачиваний:
107
Добавлен:
24.02.2016
Размер:
2.8 Mб
Скачать

Лекция 18. Связанные списки на основе рекурсивных данных

18.1. Что такое стек и очередь

Ранее мы рассмотрели простейшую форму организации списка - на ос­нове динамических массивов данных. Недостатком таких списков является то, что при добавлении или удалении элементов в список приходится копиро­вать динамические массивы и менять размер выделенной памяти, чтобы пере­строить массив, сдвигая на одну позицию все элементы расположенные пра­вее места вставки (удаления). Более эффективная организация списка, не тре­бующая копирования динамических массивов, может осуществляться на ос­нове рекурсивного типа данных. Рекурсивный тип данных при своем описа­нии допускает обращение к самому себе. В Pascal рекурсивный тип данных представляет собой запись, содержащую набор полей для хранения информа­ционной части элемента и одного (односвязный) или двух (двухсвязный спи­сок) полей, представляющих собой указатели (т. е. адреса) на соседний или соседние элементы списка.

Связанный список - это структура данных в виде списка, элементы которого связаны друг с другом с помощью указателей. Наибольшее рас­пространение получили две формы списка - стек и очередь.

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

Очередь - это список с двумя точками входа. С одной стороны после­довательности данные добавляются в список (в конец очереди), с другой сто­роны списка данные удаляются из него (из начала очереди). Таким образом, реализуется принцип - «первым вошел - первым вышел». Наглядный пример - трубка, открытая с обеих сторон, заполняемая шариками, имеющими диа­метр, равный внутреннему диаметру трубки. С одной стороны трубки шарики добавляются в нее, с другой вынимаются.

18.2. Понятие рекурсивных данных и однонаправленные списки

Рекурсивный тип данных для односвязных списков имеет вид:

Type Tinf=integer; //Описание типа информационной части TSel=ASel; //Описание типа указателя на элемент Sel=record // односвязных списков inf:Tinf; a:TSel; end;

Тип Tinf содержит описание информационной части элемента спи­ска, которая в каждом конкретном случае своя. В данном случае и при после­дующем изложении материала будет использована эта простейшая структура информационной части. Не следует забывать переопределить её в той или иной конкретной программе.

Как видим, описание типа TSel содержит внутри обращение к самому себе (a:TSel) , т.е. оно рекурсивно. При этом a является указателем на ячейку памяти точно такой же структуры. В поле inf размещается информа­ционная часть элемента списка, причем, по крайней мере, в одном из полей inf, а иногда и в отдельном поле записи расположены сведения, по которым производится поиск или сортировка требуемой информации. Это поле будем обозначать key:Tkey, а сведения называть ключом.

С помощью такого рекурсивного типа организуются однонаправленные связанные списки следующим образом: элементы списка размещаются в ячейках памяти типа TSel, причем в поле a каждой ячейки помещается ад­рес следующей за ней ячейки (рис. 18.1.):

spi sp2 sp3 sp k-1 sp k

a1

a2

a3

► ►

ak-1

ak

infi

inf2

inf3

infk-1

infk

Рис 18.1.

Все ячейки динамически размещаются в куче по адресам sp1, sp2, spk. При такой организации списков очень просто удалять или вставлять но­вые ячейки. Так, чтобы удалить ячейку с адресом sp2, в адресную часть предшествующей ячейки нужно занести адрес последующей ячейки и осво­бодить память, занимаемую удаляемой ячейкой ( sp1A.a:=sp3; dispose(sp2); см. рис 18.2.).

infk

Рис 18.2.

Для добавлении новой ячейки между ячейками с адресами sp1 и sp2 необ­ходимо создать новую ячейку, занести в нее информационную часть, в адрес­ную часть предшествующей ячейки занести адрес новой ячейки, а в адресную часть новой ячейки адрес последующей ячейки (new(sp); spA.inf:=inf; sp!A.a:=sp; spA.a:=sp2; см. рис. 18.3.).

sp1 sp2

a1

V »

a2

\ /

inf1

inf2

aвст

infвст

Рис. 18.3.

Адресная часть последней ячейки spkA.a:=nil; Для стека задается вер­шина стека, равная адресу 1-й ячейки (в обозначениях рис. 18.1.) w:=sp1;, а для односвязной очереди адрес начала очереди w1:=sp1; и адрес конца оче­реди (адрес последней ячейки) wk:=spk; .

Пример. Создать стек, содержащий целые числа, и найти количество положительных элементов стека

Type

Tinf=integer; // Описание типа информационной части

TSel=ASel;

Sel=record

inf:Tinf;

a:TSel;

end;

var k,n,kol:integer; w,t:TSel;

begin

n:=strtoint(edit1.Text); // Количество элементов стека w:= nil; Randomize; // Вершина стека

for k:=1 to n do begin

New(t); // Создаем новый элемент

tЛ.inf:=random(101)-50;//Запись информационной части tA.a:=w; // В адресную часть заносим прежнюю вершину w:=t; // Задаем новую вершину

end;

ListBoxl.Clear; t:=w; kol:=0;

while t<>nil do begin

к:=^.^^//Читаем информ. часть текущего элемента

ListBox1.items.add(inttostr(k));

if k>0 then inc(kol);

t:=tA.a; // Задаем адрес следующего элемента end;

label1.Caption:='Кол. полож. элементов стека = '

+inttostr(kol);

end;

Для работы с односвязными списками необходимо создать, например, модуль List1S, содержащий описание рекурсивного типа и набор специали­зированных процедур.