Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Пособие часть 1.doc
Скачиваний:
60
Добавлен:
24.09.2019
Размер:
6.98 Mб
Скачать

3.5. Бинарные деревья как атд

Аналогично спискам, для обработки деревьев можно использовать как рекурсивный, так и итерационный подход, поэтому в литературе встречаются различные варианты функциональной спецификации для бинарных деревьев [2, 3]. Воспользуемся рекурсивным подходом как более универсальным, при этом несколько модифицируем набор операций, который вводился ранее для обработки линейных и иерархических списков.

Введем АТД BinTree — сокращенно BT( α ), где α  — тип данных, которые хранятся в узлах бинарного дерева . Считаем, что значение типа BT есть либо  (пустое бинарное дерево), либо значение типа NonNullBT( α ). Тогда базовые операции типа BT ( α ) задаются набором функций:

0)  :  BT(α);

1) Root: NonNullBT(α)  α;

2) Left: NonNullBT (α ) BT(α);

3) Right: NonNullBT  BT(α);

4) ConsBT: α  BT(α)  BT(α)  NonNullBT(α);

5) IsNull: BT(α)  Boolean;

и набором аксиом, справедливых для всех  u типа α, b типа NonNullBT ( α ), b1, b2 типа BT ( α ) ):

A1) IsNull (  ) = true;

A1') IsNull ( b ) = false;

A2) IsNull ( ConsBT ( u , b1 , b2 ) ) = false;

A3) Root ( ConsBT ( u , b1 , b2 ) ) = u;

A4) Left ( ConsBT ( u , b1 , b2 ) ) = b1;

A5) Right ( ConsBT ( u , b1 , b2 ) ) = b2;

A6) ConsBT ( Root ( b ) , Left ( b ) , Right ( b ) ) = b.

Здесь функции Root, Left и Right  селекторы: Root выделяет корень бинарного дерева, а Left и Right  его левое и правое поддеревья соответственно. Конструктор ConsBT порождает бинарное дерево из заданных узла и двух бинарных деревьев — его сыновей. Предикат IsNull  индикатор, различающий пустое и непустое бинарные деревья.

Такого минимального набора операций достаточно при использовании языков функционального программирования. Однако для эффективной реализации средствами универсального языка программирования их явно недостаточно, хотя теоретически любую операцию можно выразить как последовательность рекурсивных вызовов данных базовых функций. Например, дерево, которое изображено на рис. 3.9,а в следующем разделе будет использоваться как пример для реализации. Если обозначиь его t, то можно записать следующее полное левое скобочное представление:

t=a(b(d ( ) e(g( ) )) с(  f(  )))

Его можно сформировать, используя функцию consbt, следующим образом:

t=consbt(a, consbt(b, consbt(d,,),consbt(e,consbt(g,,),)),consbt(c,,consbt(f,,)))

Однако, для того, чтобы добавить или удалить узел (а это типовые операции в большинстве применений деревьев), придется сначала «разобрать» дерево с помощью селекторов, а затем «собрать» с помощью конструктора. Например, для того, чтобы удалить узел g из уже сформированного дерева t, можно использовать следующую последовательность операций

t=consbt(root(t), consbt(root(left(t)), left(left(t), consbt(root(right(left(t))), ,)), right(t))

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

Примем данную спецификацию за основу, которую можно дополнить (изменить) в конкретных случаях. Теперь можно перейти к реализации.