Бинарные деревья

Бинарные деревья

Отрывок из книги Дональда Кнута "Selected Papers on Discrete Mathematics" , стр.150-153

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

Левое поддерево поизвольной вершины X содержит ключи, не превосходящие key[X] ( значение вершины X ), правое - не меньшие key[X]. Разные бинарные деревья поиска могут представлять одно и то же множество. Время выполнения ( в худшем случае ) большинства операций пропорционально высоте дерева. 
При представлении с использованием указателей для каждой вершины дерева нужно хранить помимо значения ключа key и дополнительных данных, также и указатели left, right, parent (на левое и правое поддерево, а также родителя). Если ребёнка ( или родителя - для корня ) нет, соответствующая переменная должна равняться NULL. Ключи в двоичном дереве поиска хранятся с соблюдением свойства упорядоченности: Пусть X - произвольная вершина двоичного дерева поиска. Если вершина Y находится в левом поддереве вершины X, то key[X]>=key[Y]. Если вершина Y находится в правом поддереве вершины X, то key[X]<=key[Y] 
Типовые функции при работе с бинарным деревом.
Функция печати выводит на экран все ключи, входящие в дерево. x - вершина бинарного дерева, left[x] - левое поддерево, right[x] - правое поддерево, key[x] - ключ, p[x] - родитель вершины. 
Печать(x)
Начало
1  если x не равен NULL
2  тогда Печать(left[x])
3  напечатать key[x]
4  Печать(right[x])
Конец
Процедура поиска. Получает на вход искомый ключ k и указатель x на корень поддерева, в котором производится поиск. Она возвращает указатель на вершину с ключом k ( если такая есть ) или NULL ( если такой вершины нет )
Поиск(x,k)
Начало
1  Пока x не равен NULL и k не равно key[x]
2    Начало
3        если k меньше key[x]
4            тогда x равно left[x]
5        иначе x равно right[x]			      
6    Конец	
7  Вернуть x   
Конец
Минимум и максимум. Минимальный ключ в дереве поиска можно найти, пройдя по указателям left от корня ( пока не дойдем до NULL ). Процедура возвращает указатель на минимальный элемент поддерева с корнем х.
Минимум(x)
Начало
1  Пока left[x] не равен NULL
2     Начало
3        x=left[x] 
4     Конец	
5  Вернуть x   
Конец
Максимум(x)
Начало
1  Пока right[x] не равен NULL
2     Начало
3        x=right[x] 
4     Конец	
5  Вернуть x   
Конец
Данный принцип демонстрируется на рисунке. 
При поиске ключа 13 мы идём от корня по пути 15->6->7->13. Чтобы найти минимальный ключ 2, мы всё время идём налево. Чтобы найти максимальный ключ 20 направо. 
Следующий и предыдущий элементы. Процедура возвращает указатель на следующий за x элемент или NULL в случае, если элемент x - последний в дереве.
ПолучитьСледующийЭлемент(x)
Начало
1  если right[x] не равен NULL
2     тогда вернуть Минимум(right[x])   
3  y равно p[x]   
4  пока y не равно NULL и x равно right[x]	  
5     Начало
6        x равно y
7        y равно p[y]
8     Вернуть y
Конец
ПолучитьПредыдущийЭлемент(x)
Начало
1  если left[x] не равен NULL
2     тогда вернуть Максимум(left[x])   
3  y равно p[x]   
4  пока y не равно NULL и x равно left[x]	  
5     Начало
6        x равно y
7        y равно p[y]
8     Вернуть y
Конец
Добавление.
Процедура добавляет заданный элемент в подходящее место дерево (такое место единственное), сохраняя свойство упорядоченности. Параметром процедуры является указатель z на новую вершину, в которую помещены значения key[z] ( добавляемое значение ключа), left[z]=NULL и right[z]=NULL. В ходе работы процедура меняет дерево T и возможно некоторые поля вершины z, после чего новая вершина c данным значением ключа оказывается вставленной в подходящее место (см. рисунок). 
Вставка(T,z)
Начало
1  y равно NULL
2  x равно root[T]
3  пока x не равно NULL
4    Начало
5      y равно x
6      если  key[z] меньше key[x]
7         тогда x равно left[x]
8      иначе x равно right[x]
9    Конец
10  p[z] равно y
11  если y равно NULL
12     тогда root[T]  равно z
13  иначе если key[z] меньше key[y]
14     тогда left[y] равно z
15  иначе right[y] равно z 	
16
Конец

Удаление. Параметром процедуры удаления явяляется указатель на удаляемую вершину. При удалении возможны случаи, указанные на рисунке ниже. Если у z нет детей, для удаления z достаточно поместить NULL в соответствующее поле его родителя ( вместо z ). Если у z есть один ребёнок, можно вырезать z, соединив его родителя напрямую с ребенком. Если же детей двое, требуются некоторые приготовления: мы находим следующий ( в смысле порядка на ключах) за z элемент y; у него нет левого ребёнка. Теперь можно скопировать ключ и дополнительные данные из вершины y в вершину z, а саму вершину y удалить описанным образом.

Удаление(T,z)
Начало
1   если left[z] равно NULL или right[z]=NULL
2      тогда y равно z
3   иначе y равно ПолучитьСледующийЭлемент(z)
4  если left[y] не равно NULL
5      тогда x равно left[y]
6  иначе x равно right[y]	
7  если x не равно NULL   	
8      тогда p[x] равно p[y]
9
10  если p[y] равно NULL
11     тогда root[T] равно x
12  иначе если y равно left[p[y]]	
13     тогда left[p[y]] равно x
14  иначе right[p[y]] равно x 	
15  если y не равен z
16     тогда key[z] равно key[y]
17     // Копируем дополнительные данные связанные с y
18  Удалить y 
Конец