Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Алгоритмические языки высокого уровня. Курс лекций

.pdf
Скачиваний:
0
Добавлен:
12.08.2026
Размер:
940 Кб
Скачать

Статические структуры данных относятся к разряду неприми-

тивных структур, которые представляют собой структурированное множество примитивных, базовых, структур. Например, вектор мо- жет быть представлен упорядоченным множеством чисел. Поскольку по определению статические структуры отличаются отсутствием из- менчивости, память для них выделяется один раз и ее объем остается неизменным до уничтожения структуры. Слово «статический» от- носится скорее к реализации структуры, нежели к абстрактному типу данных.

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

Динамические структуры данных по определению характери-

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

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

информационного поля (полей), в котором содержатся данные (в общем случае информационное поле само может являться слож- ной интегрированной структурой вектором, массивом, другой ди- намической структурой и т.п.);

поля ссылок (связок), в котором содержатся один или несколько указателей, связывающих данный элемент с другими элементами структуры.

Когда связное представление данных используется для решения прикладной задачи, для конечного пользователя «видимым» делается

101

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

Достоинства связного представления данных:

возможность обеспечения изменчивости структур;

размер структуры ограничивается только доступным объемом памяти ПК;

при изменении логической последовательности элементов структуры требуется не перемещение данных в памяти, а только кор- рекция указателей;

большая гибкость структуры.

Вместе с тем связное представление имеет и недостатки, основ- ные из которых:

на поля связок расходуется дополнительная память;

доступ к элементам связной структуры может быть менее эф- фективным по времени.

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

Списком называется упорядоченное множество, состоящее из пе- ременного числа элементов, к которым применимы операции вклю- чения, исключения. Список, отражающий отношения соседства меж- ду элементами, называется линейным. Длина списка равна числу

102

элементов, содержащихся в списке, список нулевой длины называет- ся пустым списком. Линейные связные списки являются простей- шими динамическими структурами данных (графически связи в спи- сках удобно изображать с помощью стрелок. Если компонента не связана ни с какой другой, то в поле указателя записывают значение, не указывающее ни на какой элемент. Такая ссылка обозначается специальным именем nil).

Структура односвязного списка. Поле INF – информацион-

ное поле, данные, NEXT указатель на следующий элемент списка.

Каждый список должен иметь особый элемент, называемый ука- зателем начала списка или головой списка, который обычно по фор- мату отличен от остальных элементов. В поле указателя последнего элемента списка находится специальный признак nil, свидетельст- вующий о конце списка.

Двусвязный список характеризуется наличием пары указателей в каждом элементе: на предыдущий элемент и на следующий. Плюс тут в том, что от данного элемента структуры можно пойти в обе сто- роны. Таким образом, упрощаются многие операции. Однако на указатели тратится дополнительная память.

Разновидностью рассмотренных видов линейных списков являет- ся кольцевой список, который может быть организован на основе как односвязного, так и двухсвязного списков. При этом в односвязном списке указатель последнего элемента должен указывать на первый элемент; в двухсвязном списке в первом и последнем элементах со- ответствующие указатели переопределяются.

103

При работе с такими списками несколько упрощаются некоторые процедуры. Однако при просмотре такого списка следует принять неко- торые меры предосторожности, чтобы не попасть в бесконечный цикл.

Стеком называется динамическая структура данных, у которой в каждый момент времени доступен только верхний (последний) эле- мент.

Понятие стека можно проиллюстрировать примером стопки книг: в стопку можно добавить еще одну книжку, положив ее сверху; из стопки можно взять, не разрушив ее, только верхнюю книгу. А для того чтобы достать книжку из середины стопки, необходимо сначала убрать по одной все лежащие выше нее. Последовательность обра- ботки элементов стека хорошо отражают аббревиатуры LIFO (Last In First Out – «последним вошел, первым вышел») и FILO (First In Last Out – «первым вошел, последним вышел»).

Для стека должны быть определены следующие операции:

empty(<нач_стека>):boolean

проверка стека

 

на пустоту;

add(<нач_стека>,<новый_элемент>):<нач_стека> добавление эле-

мента в стек;

Take(<нач_стека>):<тип_элементов_стека>

получение зна-

 

чения верхнего

 

элемента;

del(<нач_стека>):<нач_стека>

удаление верх-

 

него элемента из

 

стека.

104

Очередью называется динамическая структура, у которой в каж- дый момент времени доступны два элемента: первый и последний. Из начала очереди элементы можно удалять, а к концу добавлять.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

D

 

 

 

 

 

 

 

 

C

 

C

 

 

D

 

 

 

 

B

 

B

 

B

 

 

C

 

D

A

 

A

 

A

 

A

 

 

B

 

C

 

 

 

 

 

 

 

 

 

 

 

 

Примером очереди может служить, например, очередь в продук- товом магазине. Последовательность обработки элементов очереди хорошо отражают аббревиатуры LILO (Last In Last Out – «послед- ним вошел, последним вышел») и FIFO (First In First Out – «первым вошел, первым вышел»).

Для очереди должны быть определены следующие операции:

empty(<нач_очереди>):boolean

проверка очереди на пустоту;

add(<кон_очереди>,<нов_эл-

добавление элемента в конец очере-

т>):<кон_очереди>

ди;

 

 

Take_beg(<нач_очереди>):<тип_эл- получение

значения

первого эле-

тов_очереди>

мента;

 

 

Take_end(<кон_очереди>):<тип_эл- получение

значения

последнего

тов_очереди>

элемента;

 

 

del(<нач_очереди>):<нач_очереди> удаление элемента из начала очере-

ди.

Дек особый вид усложненной очереди (дек – deque – DoubleEnded QUEue – двухконцевая очередь). В каждый момент времени у дека доступны как первый, так и последний элементы, причем до- бавлять и удалять элементы можно и в начале, и в конце дека. Таким образом, дек это симметричная двусторонняя очередь. Частный случай дека дек с ограниченным входом и дек с ограниченным вы- ходом. Набор операций для дека аналогичен набору операций для очереди, с той лишь разницей, что добавление и удаление элементов можно производить и в конце, и в начале структуры (задачи, для ре- шения которых требуется использование структуры дека, встречают- ся в вычислительной технике и программировании гораздо реже, чем задачи, реализуемые на структуре стека или очереди). Примером де- ка может служить некий терминал, в который вводятся команды, ка- ждая из которых выполняется какое-то время. Если ввести следую- щую команду, не дождавшись, пока закончится выполнение преды- дущей, то она встанет в очередь и начнет выполняться, как только

105

освободится терминал. Это FIFO очередь. Если же дополнительно ввести операцию отмены последней введенной команды, то получа- ется дек).

Бинарное дерево это иерархическая динамическая структура данных, состоящая из узлов и соединяющих их дуг. В каждый узел, кроме одного, ведет ровно одна дуга. Этот единственный узел назы- вается корнем дерева.

С каждой вершиной дерева связывается конечное число отдель- ных деревьев, называемых поддеревьями. Вершина Y, находящаяся непосредственно ниже вершины X, называется непосредственным потомком X, а вершина X называется предком Y. Если вершина не имеет потомков, то она называется листом, если имеет, то называет-

ся внутренней вершиной.

Примеры различных списочных структур приведены на рис. 4.1:

a)односвязный (линейный) список: структура, каждый элемент которой «знает» адрес только следующего за ним элемента (стек, очередь);

b)двусвязный линейный список: структура, каждый элемент которой «помнит» адрес не только следующего, но и предыдущего элемента списка (дек);

c)бинарное дерево может быть представлено двусвязным нели- нейным списком.

Каждая вершина помнит обоих своих возможных потомков. Если каждой вершине необходимо помнить не только потомков, но и предка, то список становится трехсвязным;

d)для представления ориентированного графа используются ие- рархические списки комбинация из двух линейных списков: вер- шины задаются структурой, содержащей три поля, а дуги структу-

106

рой, содержащей два поля (показан ориентированный граф, пред- ставленный приведенной списочной структурой).

Рис. 4.1. Примеры списочных структур

Описание списочных структур. Каждый элемент односвязного списка хранит адрес другого элемента из этого же списка.

Описание этой структуры:

type ukazatel = ^element_spiska; element_spiska = record znachenie : integer;

next : ukazatel;

end;

Замечание: кажется, что гораздо более естественным было бы отнести поле next_element к типу pointer. Однако не- удобства, которые возникнут из-за нетипизированности указате- лей в процессе написания программы, будут гораздо серьезнее, чем одна лишняя строчка при описании типов.

Описание списочных структур

a) односвязный список

type ukazatel = ^elem_spiska;

 

elem_spiska = record

 

znach : integer;

107

b)двусвязный

список

next : ukazatel; end;

линейный type point = ^element_spiska; list = record znachenie : in-

teger;

sled : point pred : point;

end;

с) бинарное дерево хический список)

(иерар- type point = ^tree; tree = record

data : integer; left_sibling : point; right_sibling: point;

end;

d)ориентированный граф (двусвязный нелинейный список)

type uk_versh = ^versh; uk_duga = ^duga;

vershina = record nomer : integer;

sled_versh : uk_versh; spisok_dug : uk_duga; end;

duga = record konec_dugi : uk_versh; sled_duga : uk_duga; end;

Оперирование элементами списка

Хранение списка. Для того чтобы хранить информацию обо всем списке, достаточно только одной (нескольких) переменной указа- теля на первый элемент (последний и т.п.) этого списка. Обычно его называют головой (началом) списка. Указатель на голову должен быть выделенным: с ним нельзя производить никаких действий, ко- торые могут стать причиной утери всего списка.

Для работы со списком обычно заводят вспомогательный указа- тель (несколько указателей) var head,p,q: ukazatel;

Обращение к элементам списка. Если есть указатель, указы-

вающий на некоторый элемент списка, то содержимое полей этого элемента и даже следующих за ним можно получить так:

P

адрес текущего элемента списка;

p^

запись из нескольких полей, хранящаяся по

 

адресу p;

p^.znachenie

значение первого поля этой записи;

p^.next_element

значение второго поля этой записи, являю-

 

щееся адресом следующего элемента списка;

108

p^.next_element^.znachenie значение, хранящееся в первом поле элемента списка, следующего за тем, на который ука- зывает р.

Создание списка. Чтобы использовать список, надо определить тип компонентов списка и переменную указатель на первый эле- мент списка. После создания необходимо поместить объект в список. Добавлять элементы можно в начало, конец или в нужное место спи- ска. Во всех этих случаях необходимо корректировать указатели. Чтобы добавлять элемент в упорядоченный список, нужно найти узел, за которым будет следовать добавляемый элемент, и устано- вить его указатель на новый элемент, а указатель нового узла устано- вить на узел, перед которым он вставляется.

Var Head, x : ukazatel;

Создание первого элемента:

New(Head); { выделяем место в памяти для перемен-

ной Head }

Head^.Next:=nil; { указатель на следующий эле- мент пуст (такого элемента нет) }

Head^.Znach:=3; { заполняем информационное поле первого элемента }

Создание следующего элемента нужно добавить элемент в ко- нец списка.

109

Введем вспомогательную переменную указательного типа, кото- рая будет хранить адрес последнего элемента списка:

x:=Head; {сейчас последний элемент списка сов- падает с его началом}

New(x^. Next); {выделим области памяти для сле- дующего (2-го) элемента и поместим его адрес в адресную часть предыдущего (1-го) элемента }

x := x^. Next; {переменная x принимает значение адреса выделенной области, таким образом осущест- вляется переход к следующему (2-му) элементу спи- ска }

x^. Znach:=5; { значение этого элемента } x^. Next:=nil; {следующего значения нет }

Остальные элементы списка создаются аналогично:

New(x^.Next); {выделим области памяти для сле- дующего элемента}

x := x^.Next; { переход к следующему (3-му) элементу списка }

x^. Znach:=1; { значение этого элемента } x^.Next:=nil; {следующего значения нет } New(x^.Next); { выделим области памяти для сле-

дующего элемента }

x := x^.Next; { переход к следующему (4-му) элементу списка }

x^. Znach:=9; { значение этого элемента }

110

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]