Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Алгор_ТХТК_пособие.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
1.6 Mб
Скачать

10.6 Деревья

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

Рисунок 10.5 Пример графа

Связанный граф, в котором нет циклов, называется деревом (рис. 10.6, 10.7). Рекурсивное определение дерева выглядит следующим образом: дерево либо пусто, либо состоит из элемента, содержащего указатели на непересекаю­щиеся деревья, называемые поддеревьями. Элементы, в которые не входит никаких ветвей, называются корневыми. Элементы, из которых не выходят ветви, называются листьями. То, что для списка принято называть элементом, для дерева часто называют узлом.

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

  1. имеется только одна вершина, в которую не входит ни одного ребра (эта вершина называется корнем двоичного дерева);

  2. в каждую вершину, кроме корня, входит одно ребро;

  3. из каждой вершины (включая корень) исходит не более двух ребер.

Рисунок 10.6 - пример двоичного дерева.

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

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

Type

Uk = ^knot;

knot = Record

date : {необходимый тип данных - информационная часть узла}

Left, Right : Uk

End;

Var Tree : Uk;

Объектами типа knot являются записи, в которых каждое из полей Left или Right есть либо Nil, либо ссылка на конкретное место в памяти, отведенное с помощью New для объекта типа knot. Дерево в программе можно представить в виде множества объектов типа knot, связанных ссылками. Сами эти объекты будут вершинами дерева, а ссылки на места в памяти, отведенные для объектов типа knot, - ребрами дерева. Если при этом поле Left (Right) некоторого объекта типа knot есть Nil, то это значит, что в дереве из данной вершины не исходит ребро, направленное влево вниз (вправо вниз).

На рис. 10.7 представлено дерево, имеющее тип Integer в информационной части узла.

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

Существует три способа просмотра всех элементов двоичного дерева:

  1. прямой просмотр - элемент; левая ветвь; правая ветвь;

  2. обратный просмотр - левая ветвь; элемент; правая ветвь;

  3. концевой просмотр - левая ветвь; правая ветвь; элемент. Просмотр следует начинать от корня рекурсивно для каждого узла. При просмотре дерева, изображенного на рис. 10.6, по первому методу получим последовательность ABDGHECFIJ. По второму методу -GDHEACIFJ.

Рисунок 10.7 - Упорядоченное двоичное дерево

Пример 10.6 Рассмотрим программу формирования двоичного дерева поиска, представляющего собой частотный словарь. Пусть с клавиатуры вводятся слова (латинскими буквами). Выведем в алфавитном порядке все введенные слова (каждое слово выводится только по одному разу) с числом встречаемости слова во введенной последовательности. Для хранения слов и частоты их встречаемости будем использовать двоичное дерево поиска. Ключом поиска будут сами слова: меньшие по алфавиту будем помещать в левое поддерево, большие - в правое. Для вывода слов в алфавитном порядке понадобится обратный обход дерева.

Формирование нового узла:

New(p); p^.word := w; p^.count := 1; p^.left := nil; p^.right := nil

Program Task15;

Type

Tree_ptr = ^Tree;

Tree = Record {Описание типа дерева}

Wordd : String; {Слово}

Count : Byte; {Частота встречаемости}

Left, Right : Treeptr; {указатели на поддеревья}

End;

Var

t, p, q : Treeptr;

W : String;

Found : Boolean;

{p, q - рабочие переменные; w - вводимое слово; found = true, если слово уже есть в дереве}

Procedure Out_tree(t: Treeptr); {Процедура вывода дерева}

{t - указатель на корень дерева}

Begin

If t <> Nil Then

Begin

Out_tree(t^.Left);

Writeln(t^. Wordd, '-',t^. count);

Out_tree(t^.Right);

End;

End;

BEGIN {Исполняемая часть программы}

Readin(w);

New(t);{Формирование корня дерева}

With t^ Do

Begin

Wordd := w;

Count := 1;

Left := nil;

Right := nil;

End;

Readln(w);

While w <>’’ Do {Признак окончания ввода - ввод пустой строки}

Begin {поиск элемента в дереве}

found := false;

p := t;

While (ponil) and (found = false) do

Begin

{Сохранить вершину, к которой будет присоединяться новый элемент}

q := р;

if w<p^.wordd then p := pMeft {Если слово меньше, то левая ветвь}

else if w>p^.wordd then p := p^.right {Если слово больше, то правая ветвь}

else

found := true; {Слово уже есть}

End; {Конец поиска в дереве}

If found then inc(р^.count) {Если слово уже есть, то изменить количество}

Else

Begin {Если слова еще нет,}

New(p); {создать новый элемент}

With р^ do Begin

wordd := w;

count := 1;

Left := Nil;

right := Nil;

End;

if w<q^.wordd Then q^.Left := p {Если меньше, то присоединить слева}

Else q^.Right := p; {Иначе справа}

End;

Readln(w); {Ввод нового слова}

End; {Конец ввода слов}

out_tree(t); {Вывод по алфавиту}

End.