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

5.3. Идеально сбалансированные деревья

В заключение данной темы рассмотрим один частный случай ДД – так называемое идеально сбалансированное дерево (ИСД). Как будет отмечено в дальнейшем, эффективное использование деревьев на практике часто требует управления ростом дерева для устранения крайних случаев, когда дерево вырождается в линейный список и тем самым теряет всю свою привлекательность (с вычислительной точки зрения, разумеется).

В этом смысле ИСД полностью оправдывает свое название, поскольку вершины в нем распределяются наиболее равномерно и тем самым ИСД имеет минимально возможную высоту. Более точно, ДД называется идеально сбалансированным, если длякаждойвершины число вершин в левом и правом ее поддеревьях отличаются не более чем на единицу. Обратим внимание, что данное условие должно выполняться для всех вершин дерева!

Это дерево - ИСД

Это – не ИСД (нарушение условия для корня!)

Это тоже не ИСД (совсем плохо, почти список!)

ИСД легко строится, если заранее известно количество вершин Nв этом дереве. В этом случае ИСД можно построить с помощью следующего рекурсивного алгоритма:

  • взять первую по порядку вершину в качестве корневой

  • найти количество вершин в левых и правых поддеревьях:

Nl = N div 2;

Nr = N – Nl – 1;

  • построить левое поддерево с Nlвершинами точно таким же образом (пока не получимNl= 0)

  • построить правое поддерево с Nrвершинами точно таким же образом (пока не получимNr= 0)

Естественно, что реализация рекурсивного алгоритма наиболее просто выполняется в виде рекурсивной подпрограммы. При этом между этой процедурой и процедурами обхода есть одно принципиальное различие: процедуры обхода лишь используютсуществующую структуру дерева, не изменяя ее, и поэтому их формальные параметры являются лишь входными, тогда как процедура построения ИСД должнаСОЗДАВАТЬвершины и каждый раз возвращать в вызвавшую ее подпрограмму адрес очередной созданной вершины. Поэтому формальный параметр ссылочного типа должен быть объявлен какпараметр-переменная. Кроме того, второй формальный параметр-значение принимает число вершин в текущем строящемся поддереве.

procedure AddNodes ( var pСurrent : Tp; aN : integer);

var pTemp : Tp;

Nl, Nr : integer;

begin

ifaN= 0then{ вершин для размещения нет }

pCurrent:=nil{ формируем пустую ссылку }

else

begin

Nl:=aNdiv2; {сколько вершин будет слева?}

Nr:=aN-Nl- 1; {сколько вершин будет справа?}

New(pTemp); {создаем корень поддерева}

AddNodes(pTemp^.left,Nl); {уходим на создание левого поддерева}

AddNodes(pTemp^.right,Nr);{уходим на создание правого поддерева}

pCurrent:=pTemp; {возвращаем адрес созданного корня}

end

end;

Запуск процесса построения как обычно выполняется из главной программы с помощью вызова AddNodes(pRoot,N). В этом вызове фактический параметрNобязательно должен иметь конкретное значение, например – заданное пользователем количество вершин в строящемся ИСД. Однако, первый фактический параметрpRoot, являясь выходным, получит свое значение лишьпосле отработкивсех рекурсивных вызовов, при возврате в главную программу.

Для понимания работы приведенной процедуры целесообразно вручную расписать шаги ее работы для простейшего дерева из трех вершин. Пусть элементами дерева являются символы A,B,C. В результате работы мы должны получить:pRoot->A,A.left->B,A.right->C,B.left->nil,B.right->nil,C.left->nil,C.right->nil.

Тогда схема построения ИСД будет:

  • Главная программа: вызов AddNodes(pRoot, 3)

  • П/п 1: Nl= 1,Nr= 1, создание вершиныA, вызовAddNodes(A.left, 1)

  • П/п 1-2: сохранение в стеке значений Nl= 1,Nr= 1, адресаA

  • П/п 1-2: Nl= 0,Nr= 0, создание вершиныB, вызов

AddNodes(B.left, 0)

  • П/п 1-2-3: сохранение в стеке значений Nl= 0,Nr= 0, адресаB

  • П/п 1-2-3: aN = 0, pCurrent = nil, возврат к 1-2

  • П/п 1-2: восстановление Nl = 0, Nr = 0, вых. параметр B.left = nil

  • П/п 1-2: вызов AddNodes (B.right, 0)

  • П/п 1-2-3: сохранение в стеке значений Nl = 0, Nr = 0, адреса B

  • П/п 1-2-3: aN = 0, pCurrent = nil, возврат к 1-2

  • П/п 1-2: восстановление Nl = 0, Nr = 0, вых. параметр B.right = nil

  • П/п 1-2: pCurrent = адрес B, возврат к 1

  • П/п 1: восстановление Nl = 1, Nr = 1, вых. параметр A.left=адрес B

  • П/п 1: вызов AddNodes (A.right, 1)

  • П/п 1-2: сохранение в стеке значений Nl = 1, Nr = 1, адреса A

  • П/п 1-2: Nl = 0, Nr = 0, создание вершины C, вызов

AddNodes(C.left, 0)

  • П/п 1-2-3: сохранение в стеке значений Nl = 0, Nr = 0, адреса C

  • П/п 1-2-3: aN = 0, pCurrent = nil, возврат к 1-2

  • П/п 1-2: восстановление Nl = 0, Nr = 0, вых. параметр C.left = nil

  • П/п 1-2: вызов AddNodes (C.right, 0)

  • П/п 1-2-3: сохранение в стеке значений Nl = 0, Nr = 0, адреса C

  • П/п 1-2-3: aN = 0, pCurrent = nil, возврат к 1-2

  • П/п 1-2: восстановление Nl = 0, Nr = 0, вых. параметр C.right = nil

  • П/п 1-2: pCurrent = адрес C, возврат к 1

  • П/п 1: восстановление Nl = 1, Nr = 1, вых. параметр A.right=адрес C

  • П/п 1: pCurrent = адрес A, возврат в главную программу

  • Главная программа: установка выходного параметра pRoot = адрес A