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

Мансуров. Основы программирования в среде Lazarus. 2010

.pdf
Скачиваний:
45
Добавлен:
27.04.2021
Размер:
6.3 Mб
Скачать

Глава 4 Типовые алгоритмы обработки информации

____________________________________________________________________

inc(SizeOfArray);

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

4.3. Динамические структуры данных

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

ных, в частности графы.

 

 

Ориентированный граф –

это система из двух множеств G (X, U) ,

где

X - множество элементов, называемых вершинами, а U - определенное

на

множестве X отношение (т.е.

U X X ), элементы которого называются ду-

гами или ребрами. Если a и b - вершины, то (a, b) - ребро. Говорят, что ребро направлено от a к b .

Неориентированный граф – это такой граф, в котором отсутствует ориен-

тация ребер, т.е. (a, b) (b, a) .

Если каждому ребру поставить в соответствие некоторое число, то это

взвешенный граф.

Вершины графа или его ребра (или те и другие) могут быть помечены. В

качестве меток могут использоваться символы или числа.

 

a

b

a

b

c

d

c

d

Рис. 4.14. Примеры графов

Путем в графе называется последовательность вершин, связанных между

331

4.3 Динамические структуры данных

____________________________________________________________________

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

Граф называется связным, если все пары вершин связаны. Будем говорить,

что граф пуст, если в нем нет вершин (а, следовательно, и ребер).

Рассмотрим теперь графы специального вида, называемыми деревьями.

Деревом называется связный граф, в котором:

1)имеется единственная особая вершина, называется корнем, в которую не заходит ни одно ребро;

2)во все остальные вершины (иначе называемых листьями, а также узлами)

заходит только одно ребро, а исходит сколько угодно ребер;

3)нет циклов (т.е. замкнутых петель)

корень

Рис. 4.15. Пример дерева

На рисунках корень указывают с помощью наглядного расположения, ко-

гда корень изображается самой верхней вершиной. С помощью дерева мы мо-

жем представить родословную некоторого человека.

Фред

Джон Пам Дэвид Элизабет

Габриэл Сью

Йэн Бабби

Билл Том Эрик

Рис. 4.16. Родословная человека по имени Фред

Этой родословной соответствует дерево вида:

332

Глава 4 Типовые алгоритмы обработки информации

____________________________________________________________________

Рис. 4.17. Дерево, соответствующее родословной Фреда

Поэтому вершины нижних уровней называют еще потомками или сыновь-

ями.

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

жение: X+Y*Z-A*B+C/D

При переводе этого выражения на внутренний язык компилятор строит де-

рево вида:

+

+

* * /

X Y Z A B C D

Рис. 4.18. Дерево, построенное компилятором при разборе выражения

Любая иерархическая структура может быть представлена в виде дерева.

Рассмотрим упрощенную структуру типичного университета.

333

4.3 Динамические структуры данных

____________________________________________________________________

Ректор

 

 

 

 

 

 

 

 

 

 

 

 

 

 

проректор по

 

проректор по эко-

 

 

проректор по науке

 

проректор по

 

кадры

учебной работе

 

номике

 

 

и внешним связям

 

АХЧ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

факультеты

бухгалтерия

экономический

 

учебные корпу-

преподаватели

 

 

отдел

 

са, общежитие

и сотрудники

 

 

 

 

 

 

 

кафедры

студенты

Рис. 4.19. Структура типичного университета

Нарисовать соответствующее дерево предоставляется самому читателю.

Количество ребер выходящих из любой вершины называется степенью узла или степенью вершины. Если из вершины не выходит ни одного ребра,

степень такой вершины равна нулю.

Важнейшим классом деревьев, чаще всего используемых в программиро-

вании являются так называемые двоичные или бинарные деревья. Двоичным деревом называется такое дерево, из каждой вершины которого выходит не бо-

лее двух ребер, т.е. в степень двоичного дерева не превышает двух. Строгое би-

нарное дерево состоит только из узлов, имеющих степень два или степень ноль.

Нестрогое бинарное дерево содержит узлы со степенью равной 0, 1 или 2.

В бинарном дереве на каждом уровне n может быть не более 2n-1 вершин.

Бинарное дерево называется полным, если в строгом бинарном дереве на каждом n-м уровне содержатся все 2n-1 вершин, рис. 4.20.

334

Глава 4 Типовые алгоритмы обработки информации

____________________________________________________________________

Рис. 4.20. Бинарное (двоичное) дерево

Наиболее часто используемые действия над деревьями – это обход дерева.

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

 

1

2

5

 

3

4

6

7

 

 

Рис. 4.21. Обход дерева

1)обход сверху, при этом сначала обрабатывают корень, затем левое подде-

рево, и затем правое поддерево. Тогда будут напечатаны 1, 2, 3, 4, 5, 6, 7;

2)обход слева. Здесь сначала обрабатывают левое поддерево, затем корень,

затем правое поддерево. Будут напечатаны 3, 2, 4, 1, 6, 5, 7;

3)обход снизу. Здесь обрабатываются сначала левое поддерево, затем пра-

вое поддерево, затем корень. Будут напечатаны 3, 4, 2, 6, 7, 5, 1.

Обход слева часто используется в алгоритмах сортировки, при работе с таблицами.

Рассмотрим еще одну информационную структуру – стек.

Стек это динамическая структура, приспособленная для того, чтобы до-

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

335

4.3 Динамические структуры данных

____________________________________________________________________

мент, помещенный в него последним. Стек работает по принципу «последним

пришел, первым ушел» ("Last In – First Out", LIFO).

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

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

ней, первой пойдет в "дело".

Рассмотрим процесс помещения в стек трех элементов a, b, c. Первона-

чально стек пусть будет пустым. На рисунке 4.22 показаны последовательные

состояния стека при добавлении элементов a, b, c.

 

 

 

 

 

 

 

 

 

 

 

верхушка

 

 

a

 

верхушка

 

 

b

 

 

 

 

 

 

 

 

a

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

c верхушка

b

a

Рис. 4.22. Последовательные состояния стека при добавлении элементов

Таким образом, последний добавленный в стек элемент с оказывается в верхушке стека. Применение стека рассмотрим на примере составления алго-

ритма обхода двоичного дерева слева.

336

Глава 4 Типовые алгоритмы обработки информации

____________________________________________________________________

 

 

 

 

 

 

начало

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

да

 

 

 

 

 

 

 

 

 

 

дерево-пусто?

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

нет

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

да

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Есть левое

 

 

 

 

 

Поместить ко-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

поддерево ?

 

 

 

 

 

 

 

рень в стек

 

 

 

 

 

 

 

 

 

нет

 

 

 

 

Дерево:=левое

 

 

 

 

 

 

 

 

 

 

 

 

 

поддерево

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Обработать

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

корень

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

да

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Стек пуст?

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

нет

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Взять корень

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

из стека

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

нет

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Есть правое

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

поддерево?

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

да

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Обработать

 

 

 

 

 

 

 

 

конец

 

 

 

 

 

 

корень

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Дерево:=правое поддерево

Рис. 4.23 Алгоритм обхода двоичного дерева слева

4.3.1 Представление в памяти компьютера динамических структур.

Память компьютера состоит из ячеек, называемых байтами. Каждый байт имеет свой номер или иначе адрес. Нумерация идет от 0 до N-1. Где N объем оперативной памяти. Если мы хотим записать в память компьютера значение некоторой переменной, то мы не указываем конкретный адрес, куда должно быть записано значение переменной. Мы просто даем этой переменной имя, т.е.

обозначаем ее, например А. Компилятор отведет этой переменной нужное ко-

личество байтов. Каждая переменная в памяти занимает в зависимости от ее типа определенное количество байтов, расположенных подряд. При этом, адре-

337

4.3 Динамические структуры данных

____________________________________________________________________

сом переменной считается адрес ее первого байта. Таким образом, под А мы подразумеваем не само значение переменной, а ее адрес (адрес первого байта).

Хотя в программе мы пишем, например А:= 28; на самом деле мы говорим компьютеру: запиши в байты памяти начиная с номера А число 28. Перемен-

ной А компилятор отведет четыре подряд расположенных байта, поскольку число 28 является целым.

Если мы хотим представить в памяти некоторый массив, то под этот мас-

сив компилятор также отведет группу подряд расположенных байтов. Пусть этому массиву присвоено имя M, количество элементов массива 20, тип массива

– целый, т.е. каждый его элемент это целые числа.

Тогда компилятор отведет под этот массив 20*4=80 байтов. Причем для обращения к отдельным элементам массива используется индекс M[1], M[I],B[I+K], т.е. в этом случае адрес состоит из двух частей: одна часть ука-

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

Таким образом:

Полный адрес = базовый адрес + смещение

338

Глава 4 Типовые алгоритмы обработки информации

____________________________________________________________________

Байты памяти

0

Переменной А отведено 4 байта.

Массиву М отведено

20х4 = 80 байтов.

N-1

Рис. 4.24. Схема распределения памяти переменной А и массиву М

Такая совокупность называется вектором памяти. В языке Pascal масси-

вы (как одномерные, так и многомерные) представляются векторами.

Но можно использовать и другой способ представления информации.

Как известно, последовательности байтов памяти мы можем интерпрети-

ровать как угодно, в частности их можно интерпретировать как адрес некото-

рой другой совокупности байтов. Например, значение переменной A: <А> = 2000← можно считать адресом

Переменная, значением которой является некоторый адрес памяти, назы-

вается указателем, а адрес, на который указывает указатель, называется звеном.

Если имеется некоторая последовательность указателей и звеньев, то ее назы-

вают цепочкой или сцеплением звеньев.

Рассмотрим простейшую структуру сцепления – связанный список. Пред-

ставим, например, с помощью связанного списка строку символов "МАМА".

339

4.3 Динамические структуры данных

____________________________________________________________________

Адрес

K

 

K+1

 

 

 

 

 

 

 

 

Элемент

 

 

 

 

 

Адрес следующего элемента

 

 

 

М

L

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

L

 

L+1

 

S

 

S+1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

A

S

 

 

M

P

 

 

 

 

 

 

 

 

 

 

 

 

 

 

P

P+1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

A

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Признак конца списка

Рис. 4.25. Связанный список

Как видно из рисунка связанный список состоит из двух полей. Первое по-

ле содержит непосредственно сам элемент списка. Второе поле – указатель на следующий элемент списка. Причем поле указателя последнего элемента спи-

ска содержит специальный признак конца списка – nil.

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

Связанные списки еще называют линейными списками.

Стек и деревья можно реализовать как с помощью векторов памяти (мас-

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

4.3.2 Реализация стека с помощью массивов

Напишем программу работы со стеком. Существует всего две операции со стеком – поместить элемент в стек и извлечь элемент из стека. Используются также термины втолкнуть (затолкнуть) элемент в стек и вытолкнуть элемент из стека. Процедуру, реализующую операцию помещения элемента в стек назовем

Put_Stack, а процедуру, реализующую процесс извлечения элемента из стека

Take_Stack. Вполне можно было бы назвать эти процедуры и Push_Stack,

340