- •Тема 2. ЕТАПИ РОЗВИТКУ ТЕОРІЇ АЛГОРИТМІВ ТА ЇЇ ЗАСНОВНИКИ
- •Тема 3. МОДЕЛІ ОБЧИСЛЕНЬ
- •Тема 4. ПОНЯТТЯ СТРУКТУР ДАНИХ
- •Тема 5 СТРУКТУРНІСТЬ ДАНИХ І ТЕХНОЛОГІЯ ПРОГРАМУВАННЯ
- •Тема 6. ІНФОРМАЦІЙНА МОДЕЛЬ
- •Тема 7. ПОКАЖЧИКИ ТА ОПЕРАЦІЇ НАД НИМИ
- •Тема 8. ФІЗИЧНА СТРУКТУРА ПОКАЖЧИКА
- •Тема 9. ПРЕДСТАВЛЕННЯ ПОКАЖЧИКІВ У МОВАХ ПРОГРАМУВАННЯ
- •Тема 10. ВИДІЛЕННЯ ТА ЗВІЛЬНЕННЯ ДИНАМІЧНОЇ ПАМ'ЯТІ
- •Тема 11. ПРИКЛАДИ РОБОТИ З ДИНАМІЧНИМИ ЗМІННИМИ
- •Тема 12. ЗАГАЛЬНА ХАРАКТЕРИСТИКА СПИСКОВИХ СТРУКТУР ДАНИХ
- •Тема 13. ЗВ’ЯЗНЕ ПРЕДСТАВЛЕННЯ ДАНИХ В ПАМ'ЯТІ КОМП’ЮТЕРА
- •Тема 14. СТЕКИ
- •Тема 15. МАШИННЕ ПРЕДСТАВЛЕННЯ СТЕКА І РЕАЛІЗАЦІЯ ОПЕРАЦІЙ
- •Тема 16. ЧЕРГИ
- •Тема 17. МАШИННЕ ПРЕДСТАВЛЕННЯ ЧЕРГИ. ЧЕРГИ З ПРІОРИТЕТАМИ. ДЕКИ.
- •Тема 18. ЛІНІЙНІ СПИСКИ
- •Тема 19: ДВОНАПРЯМЛЕНІ ЛІНІЙНІ СПИСКИ
- •Тема 20. ДЕРЕВА. СТВОРЕННЯ ТА ОБХІД БІНАРНОГО ДЕРЕВА
- •Тема 21: ЕЛЕМЕНТИ ТА ВЛАСТИВОСТІ БІНАРНОГО ДЕРЕВА
- •Тема 22. ОПЕРАЦІЇ З ВУЗЛАМИ ДЕРЕВА
- •Тема 23: АЛГОРИТМИ ВИЗНАЧЕННЯ ВЛАСТИВОСТЕЙ БІНАРНОГО ДЕРЕВА
- •Тема 24. ПОНЯТТЯ ГРАФА ТА ЙОГО ЗОБРАЖЕННЯ В ПАМ'ЯТІ КОМП'ЮТЕРА
- •Тема 26. ОБХІД ГРАФУ: ПОШУК ВГЛИБИНУ
- •Тема 27. ОБХІД ГРАФУ: ПОШУК УШИР
- •Тема 28. КЛАСИЧНІ АЛГОРИТМИ СОРТУВАННЯ ОДНОРІДНИХ ДАНИХ
- •Тема 29. ШВИДКІ АЛГОРИТМИ СОРТУВАННЯ ОДНОРІДНИХ ДАНИХ
- •Тема 30. КЛАСИЧНІ АЛГОРИТМИ ПОШУКУ ДАНИХ ЗА ЗАДАНИМИ КРИТЕРІЯМИ
- •Тема 31. КЛАСИФІКАЦІЯ КРИТЕРІЇВ ПОШУКУ ДАНИХ У МАСИВАХ
- •Тема 32. КРИПТОГРАФІЧНІ ЗАСОБИ ЗАХИСТУ ІНФОРМАЦІЇ
- •Тема 33. ПРОБЛЕМИ І ПЕРСПЕКТИВИ КРИПОТГРАФІЧНИХ СИСТЕМ
- •Тема 34. АЛГОРИТМИ ШИФРУВАННЯ
- •Тема 36. ПОКАЗНИКИ СКЛАДНОСТІ АЛГОРИТМІВ
Тема 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
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