Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
РП_31_АСД.pdf
Скачиваний:
166
Добавлен:
23.02.2016
Размер:
2.92 Mб
Скачать

Тема 21: ЕЛЕМЕНТИ ТА ВЛАСТИВОСТІ БІНАРНОГО ДЕРЕВА

Дерева є особливим видом розгалужених спсиків. Особливістю таких динамічних структур даних є те, що кожен елемент має декілька вказівних полів (бінарні, тернарне, парні дерева).

На відмінну від двонапрямлених списків, дерева не мають лінійної структури. Підлеглість елементів має розгалужену структуру – це означає, що такі списки мають один верхній елемент – голову. Він має декілька підлеглих елементів, які в свою чергу мають декілька елементів, причому на будь-якому рівні всі вказівники повинні бути зв’язані з іншими елементами, якщо ж ні, то в таких випадках вони вказують в nil.

Доступ до елементів дерева здійснюється послідовно з вершини дерева через фіксований вказівник top.

Елементи дерева, обидва вказівники яких зв’язані із підлеглими елементами, називають

вузловими вершинами або гілками.

Елементи дерева, обидва вказівники яких вільні, називаються листками або висячими вершинами.

Елементи дерева, один з вказівників яких вільний, називаються напівисячими вершинами. Шириною бінарного дерева називається кількість вузлів від крайнього лівого до крайнього

правого елементу.

Висотою бінарного дерева називається максимальна кількість рівнів елементів від вершини дерева до найдальшого листка.

Бінарні дерева заповнюються не довільним чином або у порядку слідування елементів, а за алгоритмом, який формує їх у вигляді впорядкованої структури.

В якості вершини дерева завжди розміщується перший по-порядку елемент. Кожен новий елемент дерева розміщується у ньому так, щоб він опинився лівіше від поточної вершини, якщо він менший від неї; і правіше, якщо він більший від неї.

Як видно з розглянутого алгоритму, структура бінарного дерева залежить від порядку введення елементів.

Оголошуються бінарні дерева подібно до лінійних списків, у вигляді вказівного типу на елемент дерева. Елемент дерева матиме три поля: інформаційне довільного типу і два вказівних відповідно на лівий і правий елементи.

USES CRT; TYPE

bintree=^el_bintree; el_bintree=record left,right:bintree; inf:integer;

END;

Дії, які можна проводити з бінарними деревами:

1.Формування.

2.Перегляд.

3.Пошук.

4.Встановлення ширини та висоти дерева.

5.Встановлення кількості вузлових вершин, листків та напівисячих вершин.

6.Доповнення новим елементом (операція вставки перед (чи після) не має змісту, оскільки будь-який елемент у дереві розміщується в строго визначеному йому місці).

7.Вилучення елемента із дерева.

69

Враховуючи, що сама структура дерева має рекурсивний характер, то всі програми, що реалізують відповідні операції теж будуть рекурсивними підпрограмами. Згідно із описаним вище алгоритмом формування бінарного дерева, рекурсивна процедура, що здійснює це, може мати вигляд:

PROCEDURE ZAPUS (var p:bintree;a:integer); BEGIN

new(p);

p^.left:=nil;

p^.right:=nil;

p^.inf:=a;

END;

PROCEDURE ROZM(var q:bintree;a:integer); BEGIN

if q^.inf<a then begin

if q^.left<>nil then ROZM (q^.left,a) else ZAPUS(q^.left,a);

end;

if q^.inf>a then begin

if q^.right<>nil then ROZM(q^.right,a) else ZAPUS(q^.right,a);

end;

END;

PROCEDURE INPUT; BEGIN

top:=nil;

writeln('введіть кількість елементів'); readln(n);

for i:=1 to n do begin

writeln('введіть елемент:'); readln(k);

if top=nil then ZAPUS(top,k) else ROZM(top,k);

end;

END;

70

Тема 22. ОПЕРАЦІЇ З ВУЗЛАМИ ДЕРЕВА

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

Вузол із заданим значенням ключа буде знайдений доволі швидко, якщо спускатися від кореня дерева бінарного пошуку за таким правилом: ліве піддерево обирається тоді, коли значення розглядуваного вузла більше за шукане, а праве піддерево – тоді, коли вказане значення менше за шукане. Якщо значення вузла дорівнює шуканому, пошук слід завершити. Якщо пошук привів до порожньої гілки дерева, то ключового значення в дереві немає. Дія алгоритму розглядається в наступному прикладі.

Розробляється функція Location знаходження ключового значення в бінарному дереві пошуку. До функції передається ключове значення і покажчик на корінь дерева, а повертає функція покажчик на вузол, значення якого дорівнює шуканому. В якості ознаки успішності пошуку використовується логічна змінна found. Пошук починається з кореня дерева і триває доти, доки шукане значення не буде знайдене або доки всі вузли дерева не будуть перевірені.

{===бінарний пошук===}

function Location(key: string; RootTree: ptr): ptr; {key - ключове значення}

{RootTree - покажчик на шуканий вузол}

var found: boolean; {ознака успішності пошуку} begin

found:=false;

while (RootTree<>nil) and (not found) do begin

if RootTree^.data=key {перевіряється значення вузла} then found:=true {значення знайдено}

else if RootTree^.data>key {значення не знайдено} then RootTree:=RootTree^.left

else RootTree:=RootTree^.right;

end;

location:=RootTree;

end;

В основній програмі здійснюється виклик функції пошуку потрібного значення вузла дерева. Щоб повторити пошук вузла, необхідно використати оператор циклу, який припинить свою роботу після натискання клавіші N.

{===головна програма обходу дерева===} begin

writeln('Create and display tree');

write('Enter number of tree''s nodes '); readln(n); {ввести кількість вузлів дерева}

root:=Tree(n);

{створити дерево}

writeln('Created tree'); writeln;

repeat

write('Enter key to search '); readln(keyfound); {ввести ключ пошуку}

71

Рис.1. Результати роботи програми
Yrok10_1. Пошук ключового значення в бінарному дереві пошуку

rootfound:=location(keyfound,root);

{виконати пошук}

if rootfound<> nil

{якщо вузол знайдено}

then writeln('found: ',rootfound^.data)

else writeln('not found');

 

write('continue? (Y/N) '); readln(ch);

{запит на продовження пошуку}

until ch='N';

 

readln;

end.

Результат роботи програми пошуку ключового значення в бінарному дереві наведений на рис.1.

Включення вузлів у дерево

Структура дерев під час роботи програм може змінюватися, тобто кількість їх елементів може збільшуватися або зменшуватися. Дерева змінної структури можна використовувати, зокрема, при створенні частотного словника. Задача створення частотного словника формулюється так: потрібно визначити, скільки разів зустрічається кожне слово в заданій послідовності слів.

Послідовність слів можна зобразити у вигляді дерева бінарного пошуку. В інформаційних полях вузла такого дерева зберігатиметься слово і кількість його повторень. У разі виявлення в тексті чергового слова дерево переглядають, починаючи з кореневого вузла.

Якщо слово в дереві знайдене, то лічильник його повторень збільшується. Але якщо слово в дереві не знайдене, воно включається в дерево із значенням лічильника, що дорівнює 1. Дану задачу називають іще пошуком із включенням.

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

У програмі, що реалізує пошук із включенням, компоненти бінарного дерева містять два інформаційних поля: поле рядкового типу для збереження слова та цілочислове поле для збереження кількості повторень слова. Оголошення типу такого компонента разом із оголошенням використаних у програмі глобальних змінних має вигляд:

type ptr=^node;

 

node=record

 

data: string;

{значення, що включається в дерево}

count: integer;

{кількість повторень значень}

left, right: ptr;

{покажчики на ліве та праве піддерево}

end;

 

var n: integer;

{кількість вузлів дерева}

root: ptr;

{покажчик на корінь дерева}

searchkey: string; {шукане слово}

ch: char;

{символ, що означає кінець процесу пошуку}

72

Розробляється процедура SearchInsert, призначена для пошуку та включення вузда у бінарне дерево. Шукане слово передається до цієї процедури через параметр-значення key, а покажчик на корінь дерева – через параметр-змінну RootTree. Після того, як роботу процедури SearchInsert буде завершено, покажчик RootTree посилатиметься на знайдений вузол. Код розробленої процедури та головної програми мають вигляд:

{===пошук і включення вузла в дерево===} procedure SearchInsert(key: string; var RootTree: ptr); {key - ключ пошуку, RootTree - покажчик на вузол} begin

if RootTree=nil then begin new(RootTree); with RootTree^ do

begin data:=key; count:=1; left:=nil; right:=nil; end;

end else

if key<RootTree^.data

then SearchInsert(key,RootTree^.left) else

if key>RootTree^.data

then SearchInsert(key,RootTree^.right) else begin

writeln('key exists'); RootTree^.count:=RootTree^.count+1; end;

end;

{===головна програма===} begin

writeln('Create and display tree');

write('Enter number of tree''s nodes '); readln(n); {ввести кількість вузлів дерева}

root:=Tree(n);

{створити дерево}

writeln('Created tree');

PrintTree(root,0);

{відобразити дерево}

writeln;

 

repeat

 

write('Enter word to search and insert '); readln(searchkey); {ввести слово для включення}

SearchInsert(searchkey, root);

{пошук із включенням}

write('continue? (Y/N) '); readln(ch);

 

until ch='N';

 

PrintTree(root,0); {відображення дерева після включення} readln;

end.

73

Результат роботи програми пошуку із включенням в бінарному дереві наведений на

рис.2.

Рис.2. Результати роботи програми

Yrok10_2. Пошук із включенням в бінарному дереві пошуку

Видалення вузлів бінарного дерева

При видаленні вузла бінарного дерева можливі три випадки:

вузол, що видаляється, не має нащадків, тобто є листком дерева;

вузол, що видаляється, має одного нащадка;

вузол, що видаляється, має двох нащадків.

Задача розв’язується найпростішим способом тоді, коли вузол, що видаляється, є листком дерева. У такому разі слід звільнити ділянку динамічної пам'яті, яку цей вузол займав, та присвоїти значення nil покажчикові на даний вузол.

Якщо видаляється вузол x, який має одного нащадка, то покажчику на цей вузол слід присвоїти адресу його нащадка і звільнити пам'ять, яку вузол x займав. Операцію видалення вузла, що має одного нащадка, проілюстровано на рис.3.

Найскладнішим є випадок, коли вузол x, що видаляється, має двох нащадків. У цьому разі на місце x слід переставити інший вузол дерева так, щоб не порушувалася властивість впорядкованості ключів. Вузол, що переставляється, називається термінальним. Один із способів визначення термінального вузла полягає у виконанні спуску по правій гілці лівого піддерева x доти, доки не буде знайдено вузла без правого нащадка. Цей вузол і є термінальним. Справді, значення цього вузла не менше за значення всіх вузлів лівого піддерева x, оскільки він належить правій гілці цього піддерева і не має правого нащадка. З іншого боку, значення знайденого в такий спосіб вузла не більше всіх вузлів правого піддерева x, оскільки він належить лівому піддереву x. Отже, значення знайденого вузла може бути записане до вузла x без порушення впорядкованості ключів. Сам термінальний вузол має бути

74

видалений після копіювання його значення до вузла x. Оскільки термінальний вузол має одного нащадка, його видалення є доволі простою операцією, яка вже розглядалася.

Дерево до і після видалення вузла D, який має двох нащадків, зображено на рис.4.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C

 

p

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

q:=p

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

B

 

 

 

 

 

 

nil

 

 

 

 

 

 

 

 

 

 

E

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

q^.left

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

A

nil

 

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

a

 

D

nil

 

nil

 

 

 

 

 

F

nil

 

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C

 

p

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

p:=q^.left

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

A

 

nil

 

nil

 

 

 

 

 

 

 

 

 

 

 

E

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

b

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

D

 

nil

 

nil

 

 

 

 

F

 

nil

 

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис.3 Бінарне дерево до (а) і після (б) видалення вузла з одним нащадком

 

 

 

 

 

 

 

 

 

 

 

p

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C

 

p

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

q:=p

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

q^.right

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

q^.left

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

B

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

F

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

A

nil nil

C

 

nil

 

nil

 

 

 

 

 

 

 

 

 

 

 

E

 

nil

 

nil

 

 

 

G

 

nil

 

nil

a

q^data:=r^.data

q:=r C

 

r:=r^.left

 

B

 

nil

 

F

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

dispose(q)

 

 

 

 

 

 

 

 

 

A

 

nil

 

nil

 

 

E

 

nil

nil

 

G

nil

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

б

Рис.4. Бінарне дерево до (а) і після (б) видалення вузла з двома нащадками Видалення вузлів бінарного дерева демонструється в наступному прикладі.

Розробляється процедура SearchDelete знаходження за заданим ключем вузла бінарного дерева пошуку та видалення його, а також програма Yrok14_5, в якій ця функція використовується. У середині процедури SearchDelete оголошується локальна рекурсивна процедура CaseTwoChild, яка опрацьовуватиме той випадок, коли вузол, що видаляється, має два нащадки. Процедура CaseTwoChild матиме параметр-змінну ref, який на певному рівні рекурсії набуватиме значення покажчика на термінальний вузол. Коли термінальний вузол буде знайдено, його значення скопіюється до вузла delNode^, який видаляється, а покажчику ref

75

присвоїться адреса лівого нащадка термінального вузла. Код програми (без процедур створення Tree та відображення PrintTree дерева) має наступний вигляд:

program Yrok10_3;

{Видалення вузла бінарного дерева} {$APPTYPE CONSOLE}

uses SysUtils;

 

type ptr=^node;

 

node=record

 

data: string;

{значення, що включається в дерево}

left, right: ptr;

{покажчики на ліве та праве піддерево}

end;

 

var n: integer;

{кількість вузлів дерева}

root: ptr;

{покажчик на корінь дерева}

searchkey: string; {шукане слово}

ch: char;

{символ, що означає кінець процесу пошуку}

{===створення бінарного дерева===} {===відображення дерева===} {===видалення заданого вузла===}

procedure SearchDelete(key: string; var pNode: ptr);

{key - ключ пошуку, pNode - покажчик на вузол, що видаляється} var delNode: ptr;

{---пошук листка і переміщення його вмісту у вузол, що видаляється---} procedure CaseTwoChild(var ref:ptr);

begin

 

if ref^.right<>nil

{спуск по правій гілці}

then CaseTwoChild(ref^.right)

else

 

begin

{заміна вузла, що видаляється, термінальним}

delNode^.data:=ref^.data;

DelNode:=ref;

ref:=ref^.left;

end;

end;

{---тіло процедури SearchDelete---} begin

if pNode=nil

then writeln('node is not found') else

if key>pNode^.data

then SearchDelete(key, pNode^.right) else

begin delNode:=pNode; if delNode^.right=nil

then pNode:=delNode^.left else

if delNode^.left=nil

76

then pNode:=delNode^.right

else CaseTwoChild(delNode^.left); dispose(delNode);

end;

end;

{===головна програма===} begin

writeln('Create and display tree and delete node');

write('Enter number of tree''s nodes '); readln(n); {ввести кількість вузлів дерева}

root:=Tree(n);

{створити дерево}

writeln('Created tree');

PrintTree(root,0);

{відобразити дерево}

writeln; repeat

write('Enter word to search and delete '); readln(searchkey); {ввести слово для включення}

SearchDelete(searchkey, root);

{видалення вузла}

write('continue? (Y/N) '); readln(ch);

 

until ch='N';

 

 

PrintTree(root,0);

{відобразити дерево після включення}

readln;

 

 

end.

 

 

Результат роботи програми видалення вузла бінарного дерева наведений на рис.5.

Рис.5. Результати роботи програми

Yrok10_3. Видалення вузла бінарного дереві пошуку

77