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


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

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

       
   

       
   

Это дерево - ИСД Это – не ИСД (нарушение условия для корня!) Это тоже не ИСД (совсем плохо, почти список!)

 

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

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

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

Nl=Ndiv2;

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

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

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

Else

Begin

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

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

 



Дата добавления: 2020-07-18; просмотров: 425;


Поиск по сайту:

Воспользовавшись поиском можно найти нужную информацию на сайте.

Поделитесь с друзьями:

Считаете данную информацию полезной, тогда расскажите друзьям в соц. сетях.
Poznayka.org - Познайка.Орг - 2016-2024 год. Материал предоставляется для ознакомительных и учебных целей.
Генерация страницы за: 0.01 сек.