
- •Оглавление
- •Бинарные деревья
- •Основные понятия и определения.
- •Основные операции с бинарными деревьями Узел бинарного дерева
- •Обход бинарного дерева.
- •Упорядоченные деревья. Включение нового узла, поиск по дереву с включением
- •Упорядоченные деревья. Поиск заданного значения.
- •Удаление бинарного дерева.
- •Удаление узла из упорядоченного дерева
- •Пример использования упорядоченного бинарного дерева для частотного анализа данных
Обход бинарного дерева.
Для двоичных деревьев (и деревьев вообще) вводиться важная категория алгоритмов – алгоритмы обхода дерева. Такой алгоритм – это метод, позволяющий получитьдоступ к каждому узлу дерева один и только один раз.
Для каждого узла выполняются некоторые виды обработки информационной части (проверка, суммирование и т.п.), однако способ обхода не зависит от конкретных действий, выполняемых над узлом, и является общим для всех алгоритмов обработки узлов. Назовем действия, выполняемые над узлом, “Обработать корень”. При разных способах обхода дерева отдельные узлы посещаются в некотором определенном порядке.
Существуют три порядка, использующие обход в глубину:
Сверху вниз(PreOrder):
обработать корень;
обходлевого поддерева;
обход правого поддерева;
Слева направо(InOrder):
обходлевого поддерева;
обработать корень;
обходправого поддерева;
Снизу вверх(PosOrder):
обходлевого поддерева;
обходправого поддерева;
обработать корень.
Для дерева на рис.1 три способа обхода дают следующий результат:
Обход |
Порядок посещения узлов | ||||||||||||
PreOrder |
100 |
70 |
50 |
10 |
55 |
60 |
58 |
65 |
90 |
150 |
200 |
170 |
210 |
InOrder |
10 |
50 |
55 |
58 |
60 |
65 |
70 |
90 |
100 |
150 |
170 |
200 |
210 |
PosOrder |
10 |
58 |
65 |
60 |
55 |
50 |
90 |
70 |
170 |
210 |
200 |
150 |
100 |
Эти три метода легко представить в виде рекурсивных процедур, листинг которых представлен ниже. Во всех процедурах в качестве обработки корня используется вывод значения информационного поля на экран. В качестве параметра Tree:PTreeв процедуры передается адрес корня бинарного дерева.
Листинг процедур обхода бинарного дерева.
Procedure PrintTree(Tree: PTree); // PreOrder
begin
if Tree<>nilthen begin //если дерево не пустое, то
Writeln(‘Value = ‘,Tree^.inf); // обработать узел
PrintTree(Tree^.Left); //обходлевого поддерева
PrintTree(Tree^.Right); //обходправого поддерева
end;
end;
Procedure PrintTree(Tree: PTree); // InOrder
begin
if Tree<>nil then begin // если дерево не пустое, то
PrintTree(Tree^.Left); //обходлевого поддерева
Writeln(‘Value = ‘,Tree^.inf); // обработать узел
PrintTree(Tree^.Right); //обходправого поддерева
end;
end;
Procedure PrintTree(Tree: PTree); // PosOrder
begin
if Tree<>nil then begin // если дерево не пустое, то
PrintTree(Tree^.Left); //обходлевого поддерева
PrintTree(Tree^.Right); //обходправого поддерева
Writeln(‘Value = ‘,Tree^.inf); // обработать узел
end;
end;
Ссылка Treeпередается как параметр-значение, следовательно, является локальной переменной процедуры, получающей копию фактического параметра. При ее изменении фактический параметр не измениться (т.к. процедура работает с его локальной копией). Это означает, что при передаче в качестве фактического параметра адреса корня дерева, хранящая его переменная останется неизменной.
Упорядоченные деревья. Включение нового узла, поиск по дереву с включением
Упорядоченнымназывается дерево, в котором для каждого узлаNзначение левого дочернего узла меньше, чем значение вN, а значение правого дочернего узла больше значения вN. Если в дереве могут содержаться одинаковые значения, то программист должен самостоятельно определить, влево или вправо помещать значение, равное значению в родительском узле (строгое неравенство заменить на нестрогое).
Построение упорядоченного дерева начинается с корня. Первое входящее значение помещается в корень дерева. Для последующих значений производиться сравнение со значением в очередном узле, начиная с корня. Если оно меньше (или равно) значению в узле, то переходим в левое поддерево, иначе – в правое. Эти переходы и сравнения продолжаются до тех пор, пока мы не придем к ссылке, которая равна nil. Тогда создается новый узел, заполняются его поля: информационное - новым значением, ссылки на левое и правое поддеревья пустым значениеnil. Адрес нового узла передается в одно из ссылочных полей родительского узла. Для этого необходимо воспользоваться параметром-переменной. Этот алгоритм реализован в виде рекурсивной процедурыInsert.
Рис.2 Добавление нового узла в упорядоченное дерево.
Листинг процедуры добавления нового узла в упорядоченное дерево.
{Параметры процедуры: Tree– указатель на корень дерева,NewValue– значение для добавляемого узла.}
Procedure Insert(var Tree:PTree; NewValue:integer);
varTemp:PTree;
begin
if Tree = nil then begin
new(Tree); // создание нового узла, в том числе и корневого
Tree^.inf:=NewValue; Tree^.Left:=nil; Tree^.Right:=nil; // заполнение полей
end
else
if NewValue <=Tree^.inf then
Insert(Tree^.left,NewValue)
{переход к левому поддереву}
else
Insert(Tree^.Right,NewValue)
{переход к правому поддереву}
end;
Следует отметить, что в процедуре InsetrпараметрTreeпередается как параметр-переменная, а не как параметр-значение в процедуреPrintTree. Это очень важно, так как в случае включения нового узла параметру-переменнойTreeприсваивается адрес нового узла и через нее передает в родительский узел ссылкы (адрес) на включенный узел, изменяя старое значение равноеnil.Само по себе создание бинарного дерева тривиально. В простейшем случае корневой узел бинарного дерева определяет все бинарное дерево. В программе необходимо описать указатель на корень дерева (например, var Root:PTree;). В начале программы необходимо провести инициализацию бинарного дерева – указателю на корень присвоить пустое значение (Root:=nil;). Бинарного дерева не существует, поэтому это пустое значение служит начальным значением бинарного дерева. Добавление новых элементов в дерево будем осуществлять процедурой Insert. Например, добавление числа Х в бинарное дерево будет выглядеть так: Insert(Root,X). Вывод на экран содержимого дерева можно записать процедурой PrintTree(Root).
Если построено упорядоченное дерево, то его можно применять для сортировки данных. Осуществляя обход дерева InOrderможно получить значения узлов в порядке возрастания, т.е. отсортированную последовательность. Для вывода значений узлов в порядке убывания необходимо внести небольшие изменения в процедуру обхода – поменять местами обходы левого и правого поддеревьев:
Procedure PrintTree1(Tree: PTree); // InOrder
begin
if Tree<>nil then begin // если дерево не пустое, то
PrintTree1(Tree^.Right); //обходправого поддерева
Writeln(‘Value = ‘,Tree^.inf); // обработать узел
PrintTree1(Tree^.Left); // обход левого поддерева
end;
end;