Опубликован: 26.09.2006 | Доступ: свободный | Студентов: 1772 / 459 | Оценка: 4.25 / 4.12 | Длительность: 17:09:00
ISBN: 978-5-9556-0066-6
Специальности: Программист, Математик
Лекция 10:

Поисковые деревья

< Лекция 9 || Лекция 10: 123456 || Лекция 11 >

Операции с двоичным поисковым деревом

Процедура {\rm
Walk(x)} обходит все узлы поддерева с корнем в узле x и печатает их ключи в неубывающем порядке:

\formula{
\t pocedure\ \t{Walk}(x);\\
\t begin\\
\mbox{}\q \t if\ (x \ne {\rm nil})\
\t then\
\{{\rm Walk}({\rm left}[x]);\ {\rm write}({\rm key}[x]);\ {\rm Walk}
({\rm right}[x])\}\\
\t end;
}

Свойство упорядоченности гарантирует правильность алгоритма. Время работы на дереве с n вершинами есть \Theta(n), каждая вершина обрабатывается один раз. Оператор {\rm Walk}({\rm root}) напечатает ключи всех элементов в неубывающем порядке.

Заметим, что порядок, при котором корень предшествует узлам обоих поддеревьев, называется preorder ; порядок, в котором корень следует за ними, называется postorder.

Покажем, что двоичные поисковые деревья позволяют выполнять операции {\rm Search}, {\rm Minimum}, {\rm
Maximum}, {\rm Successor} и {\rm Predecessor} за время ( O(h), где hвысота дерева.

Поиск. Процедура поиска получает на вход искомый ключ k и указатель x на корень дерева и возвращает указатель на вершину с ключом k (если такая есть) или \rm
nil (если такой вершины нет).

\formula{
\t procedure\ {\rm Search}\ (x,
k);\\
\t begin\\
\mbox{}\q\t if\ (x = {\rm nil})\
\t{or}\ (k = {\rm key}[x])\
\t then\ {\rm exit};\\
\mbox{}\q\t if\ (k < {\rm key}[x])\
\t then\
{\rm Search}\ ({\rm left}[x], k)\ \t else\ {\rm Search}({\rm right}[x],
k)\\
\t end;
}

В процессе поиска мы двигаемся от корня, сравнивая ключ k с ключом, хранящимся в текущей вершине x. Если они равны, поиск завершается. Если k < {\rm key}[x], то поиск продолжается в левом поддереве x, если же k > {\rm key}[x], то в правом. Длина пути поиска не превосходит высоты дерева, поэтому время поиска есть O(h) (где hвысота дерева).

Итеративная версия процедуры Поиск

\formula{
\t procedure\ {\rm
IterativeSearch}\ (x,k);\\
\t begin\\
\mbox{}\q\t while\ (x \ne {\rm
nil})\ \t{and}\ (k \ne {\rm key}[x])\ \t do\\
\mbox{}\qq\t if\ k < {\rm key}[x]\
\t then\ x:= {\rm left}[x]\
\t else\ x:= {\rm right}[x];\\
\t end;
}

Минимум и Максимум. Элемент с минимальным ключом в дереве поиска можно найти, пройдя от корня по указателям {\rm left}, пока не упремся в {\rm nil}. Процедура {\rm Minimum}(x) возвращает указатель на найденный элемент поддерева с корнем x.

\formula{
\t procedure\ {\rm Minimum}(x);\\
\t begin\ \t while\ {\rm left}[x] \ne {\rm nil}\ \t do\
x:= {\rm left}[x]\ \t end;
}

Алгоритм {\rm Maximum} симметричен:

\formula{
\t procedure\ {\rm Maximum}(x);\\
\t begin\ \t while\ {\rm right}[x] \ne {\rm nil}\ \t do\
x:= {\rm right}[x]\ \t end;
}

Оба алгоритма требуют времени O(h), где hвысота дерева.

Следующий и предыдущий элементы. Если xуказатель на некоторый узел дерева, то процедура {\rm Successor}(x) возвращает указатель на узел со следующим за x элементом или {\rm
nil}, если указанный элемент — последний в дереве:

\formula{
\t procedure\ {\rm
Successor}(x);\\
\t begin\\
\mbox{}\q\t if\ ({\rm right}[x] \ne
{\rm nil})\ \t then\ {\rm return}\
{\rm Minimum}\ ({\rm right}[x]);\\
\mbox{}\q y:= p[x];\\
\mbox{}\q \t while\ (y \ne {\rm
nil})\ \t{and}\ (x={\rm right}[y])\
\t do\ \{x:= y;\ y:= {\rm
parent}[y]\};\\
\mbox{}\q{\rm return}\ y\\
\t end;
}

Приведенная процедура отдельно рассматривает два случая. Если правое поддерево вершины x не пусто, то следующий за x элемент — минимальный элемент в этом поддереве и он равен {\rm Minimum}({\rm
right}[x]). Если правое поддерево вершины x пусто, то идем от x вверх, пока не найдем вершину, являющуюся левым сыном своего родителя. Этот родитель (если он есть) и будет искомым элементом. Время работы процедуры {\rm Successor} на дереве высоты h есть O(h), так как мы двигаемся либо только вверх, либо только вниз. Процедура {\rm Predecessor} симметрична.

Добавление элемента.

Процедура {\rm Insert}(T,
z) добавляет заданный элемент в подходящее место дерева T. Параметром процедуры является указатель z на новую вершину, в которую помещены значения {\rm key}[z], {\rm left}[z] = {\rm
nil} и {\rm right}[z] = {\rm nil}. В ходе работы процедура изменяет дерево T и (возможно) некоторые поля вершины z, после чего новая вершина с данным значением ключа оказывается вставленной в подходящее место дерева:

\formula{
\t procedure\ {\rm
Insert}(T,z);\\
begin y := {\rm nil};\ x := {\rm
root};\\
\mbox{}\q\t while\ (x \ne {\rm
nil})\ \t do\\
\mbox{}\q\q \{y := x;\ \t if\ {\rm
key}[z] < {\rm key}[x]\
\t then\ x := {\rm left}[x]\
\t else\ x := {\rm right}[x]\};\\
\mbox{}\q p[z] := y;\\
\mbox{}\q \t if\ y = {\rm nil}\
\t then\
{\rm root} := z \t else\ \t if\ {\rm key}[z] < {\rm key}[y]\
\t then\ {\rm left}[y] := z\
\t else\\
\mbox{}\q\q {\rm right}[y]:= z\\
\t end;
}

Подобно процедурам {\rm Search} и {\rm
IterativeSearch}, процедура {\rm Insert} двигается вниз по дереву, начав с его корня. При этом в вершине y сохраняется указатель на родителя вершины x. Сравнивая {\rm
key}[z] с {\rm key}[x], процедура решает куда идти — налево или направо. Процесс завершается, когда x становится равным {\rm nil}. Этот {\rm nil} стоит как раз там, куда надо поместить z, что и делается. Очевидно, добавление требует времени O(h) для дерева высоты h.

Удаление элемента

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

Упражнения

  1. Напишите рекурсивный вариант процедуры {\rm
Insert}.
  2. Напишите процедуру {\rm Delete}, удаляющую элемент z из дерева T.
  3. Набор из n чисел можно отсортировать, сначала добавив их один за другим в двоичное дерево поиска с помощью процедуры {\rm Insert}, а потом обойти дерево с помощью процедуры {\rm Walk}. Оцените время работы такого алгоритма.
  4. Покажите, что если вершина двоичного дерева поиска имеет двоих детей, то следующая за ней вершина не имеет левого ребенка, а предшествующая — правого.
< Лекция 9 || Лекция 10: 123456 || Лекция 11 >
Антон Сиротинкин
Антон Сиротинкин

на стр 6, лекции 3, Очевидно "Ck <= модуль(Gk(е))*b(k+1)" (1) - , подскажите что значит "модуль" и почему это очевидно...