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

13.7. Бінарні дерева

Бінарне дерево має вид

T(v)

v

Ліве піддерево

Праве піддерево

L(v) R(v)

Рис 13.16. Структура бінарного дерева

Як і для списків, основними процедурами обробки дерев є пошук, вставка і вилучення вузла дерева. Ці процедури ми розглянемо на прикладі, в якому сортування послідовності реалізована за допомогою побудови і наступної обробки бінарного дерева.

Приклад 13.5: Нехай A = a1, a2,..., ,an - послідовність цілих чисел. Для того, щоб відсортувати послідовність А, що складається з елементів ai

а) побудуємо бінарне дерево, що задовольняє наступній властивості: для будь-якого вузла дерева v будь-який елемент L(v)  v  будь-який елемент R(v)

б) побудуємо послідовність із вузлів дерева, в якій елементи розташовані у відповідності з цим же принципом: послідовність {L(v)}, v, послідовність {R(v)}.

Легко бачити, що вихідна послідовність буде впорядкованою.

Розв’язок.

Послідовність А ми реалізуємо у виді списку. Нам знадобляться процедури:

  • побудови списку з чисел, що вводяться з клавіатури;

  • побудови дерева, що задовольняє властивості п. а);

  • побудови списку, що задовольняє властивості п. б);

  • виведення списку.

Типи елементів динамічних структур даних задачі:

Type

Point = ^ Item;

Item = Record

Key: Integer;

Next: Point

End;

Link = ^ Vertex;

Vertex = Record

Key: Integer;

Left, Right: Link;

End;

Програма розв’язку задачі:

Program ListSort;

{описання типів даних}

Var

A, B: Point;

LastB: Point; {кінець списку B}

Tree: Link;

{описання процедур}

{процедура введення списку}

{процедура побудови дерева з списку}

{процедура побудови списку з дерева}

{процедура виведення списку}

Begin {розділ операторів}

InpList(A); {процедура введення списку}

Tree := Nil;

TreeBild(Tree, A); {процедура побудови дерева Tree з списку A}

B := Nil;

ListBild(Tree, B); {процедура побудови списку B з дерева Tree}

OutList(B); {процедура виведення списку}

End. {кінець програми}

Procedure InpList(var P: Point); {процедура введення списку}

Var

Ch: Char;

Q: Point;

Begin

P := Nil; {порожній список}

Writeln('Для введення числа нажміть y');

ch := Readkey;

While ch = 'y' do begin

New(Q); {формування нового елемента списку}

Writeln('Input Item:');

Read(Q^.Key);

Q^.Next := P; {включення нового елемента в список}

P := Q; {покажчик списку - на початок списку}

Writeln('Продовжити введення? Y/N ');

ch:= Readkey

end

End;

{процедура побудови дерева з списку}

Procedure TreeBild(var T: Link; P: Point);

Var

x: Integer;

Procedure Find_Ins(var Q: Link; x: Integer);

Procedure Ins(var S: Link);

Begin {процедури вставки елемента}

New(S);

S^.Key := x;

S^.Left := Nil;

S^.Right := Nil

End; {процедури вставки елемента}

Begin {процедур пошуку і вставки елемента}

x := P^.Key;

If Q = Nil

then Ins(Q)

else if x < Q^.Key

then Find_Ins(Q^.Left, x)

else Find_Ins(Q^.Right, x)

End; {процедури пошуку і вставки елемента}

Begin {процедури побудови дерева з списку}

If P <> Nil

then begin

Find_Ins(T, P^.Key);

TreeBild(T, P^.Next)

end

End; {процедури побудови дерева з списку}

Зауважимо, що передача посилань на вершини структур, що оброблюються, здійснюється через параметри-змінні. Це дозволяє породжувати вузли дерева без використання додаткових покажчиків.

{процедура побудови списку з дерева}

Procedure ListBild(var T: Link; var F: Point);

Var

Temp: Point;

Begin

If T <> Nil

then begin

ListBild(T^.Left, F);

New(Temp); {породження нового елемента}

Temp^.Key := T^.Key;

Temp^.Next := Nil;

If F = Nil

then begin

F := Temp;

LastB := Temp

end

else begin

LastB^.Next := Temp;

LastB := Temp

end;

{змінна LastB - покажчик на останній елемент списку, що будується}

ListBild(T^.Right, LastB^.Next)

end

End; {процедури побудови списку з дерева}

Procedure OutList(var P: Point); {процедура виведення списку}

Var

x: Integer;

Begin

If P <> Nil

then begin

Write(P^.Key, ' ');

OutList(P^.Next)

end

End; {процедури виведення списку}

{процедура виведення дерева - використовувалась при налагодженні}

Procedure OutTree(var T: Link);

Begin

If T <> Nil

then begin

OutTree(T^.Left); {ліве піддерево}

Write('V = ', T^.Key); {корінь дерева}

OutTree(T^.Right); {праве піддерево}

end

End; {процедури виведення дерева}

У процедурах ListBild і OutTree використаний загальний принцип обробки дерева – так званий обхід дерева зліва - направо:

L(v) - обробка лівого піддерева;

v - обробка кореня;

R(v) - обробка правого піддерева.

Обробка L(v) і R(v) реалізована як рекурсивний виклик процедури обробки дерева. Крім обходу зліва направо, в задачах використовуються також і інші порядки переліку вузлів. Для бінарних дерев це:

L(v) - v - R(v) - зліва направо;

R(v) - v - L(v) - справа наліво;

v - L(v) - R(v) - зверху вниз;

L(v) - R(v) - v - знизу вверх.

Якщо в процедурі ListBild застосувати обхід справа наліво, елементи списку B будуть упорядкованими за зменшенням. У процедурі Find_Ins по суті застосований обхід зверху донизу у модифікації: v - L(v) або R(v) (у залежності від результату порівняння x < Q^.Key).

Якщо в виді дерева представлено арифметичний вираз (приклад 13.1), процедуру обчислення його значення треба програмувати обходом знизу-вгору: обчислити значення лівого операнду правого операнду результат бінарної операції. Неважко побачити, що якщо при побудові дерева Tree будь-яке ліве піддерево буде містити стільки же вузлів, скільки і відповідне праве, для його побудови треба O(n*log2n) порівнянь. Однак у гіршому випадку (якщо, наприклад, вихідний список упорядкований), для вставки кожного наступного вузла алгоритм буде слідувати по одній і тій же гілці (наприклад, лівій). Тому алгоритм витрачає O(n2) порівнянь, тобто не є ефективним. Однак його можна модифікувати таким чином, щоб процедура TreeBild будувала т.н. збалансоване дерево. Тоді алгоритм сортування стане ефективним.