Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Мансуров. Основы программирования в среде Lazarus. 2010

.pdf
Скачиваний:
68
Добавлен:
27.04.2021
Размер:
6.3 Mб
Скачать

Глава 4 Типовые алгоритмы обработки информации

____________________________________________________________________

p:= ptr_tree;

if not (p^.node = Elem) then begin

if p^.left <> nil then

search_node (Elem, p^.left, current_tree); if p^.right <> nil then

search_node (Elem, p^.right, current_tree);

end

else current_tree:= p;

end;

В процедуру передаются значение искомого узла Elem, само дерево

ptr_tree. Процедура возвращает ссылку на найденный узел current_tree.

Рекурсивная процедура удаления текущего поддерева:

procedure dispose_tree (ptr_tree: PTree); var

p: ^Tree; begin

if ptr_tree <> nil then begin

p:= ptr_tree;

if p^.left <> nil then begin

dispose_tree(p^.left); end;

if p^.right <> nil then begin

dispose_tree(p^.right);

381

4.3 Динамические структуры данных

____________________________________________________________________

end;

dispose(p);

end

end;

Рекурсивная процедура обхода двоичного дерева слева запишется на удив-

ление просто:

procedure obhod(p: PTree);

begin

if p<>nil then

begin

obhod(p^.left);

write(p^.node, ' ');

obhod(p^.right);

end;

end;

Реализуйте самостоятельно обход двоичного дерева слева по не рекурсив-

ному алгоритму 4.30!

Напишем программу ввода двоичного дерева с клавиатуры и его обхода слева:

program project1; {$mode objfpc}{$H+} uses

CRT, FileUtil; type

PTree= ^Tree; // Указатель на дерево

382

Глава 4 Типовые алгоритмы обработки информации

____________________________________________________________________

Tree= record

// Само дерево, имеет тип - запись

node: string;

// значение вершины (узла) дерева

left: PTree;

// Ссылка на левое поддерево

right: PTree;

// Ссылка на правое поддерево

end; var

ptr_tree, current_tree, root: ^Tree; p, current: ^Tree;

s: string; choose: integer;

{Процедура поиска узла}

procedure search_node(Elem: string; ptr_tree:PTree;

var current_tree:PTree);

var

p: ^Tree; begin

p:= ptr_tree; writeln(p^.node);

if not (p^.node = Elem) then begin

if p^.left <> nil then

search_node (Elem, p^.left, current_tree); if p^.right <> nil then

search_node (Elem, p^.right, current_tree);

end

else current_tree:= p;

end;

{Процедура вывода дерева на экран}

383

4.3 Динамические структуры данных

____________________________________________________________________

procedure view_tree (ptr_tree: PTree); var

p: ^Tree; begin

p:= ptr_tree; writeln(p^.node);

if p^.left <> nil then view_tree(p^.left); if p^.right <> nil then view_tree(p^.right);

end;

{Процедура удаления текущего поддерева} procedure dispose_tree (ptr_tree:PTree); var

p: ^Tree; begin

if ptr_tree <> nil then begin

p:= ptr_tree; writeln(p^.node);

if p^.left <> nil then begin

dispose_tree(p^.left); end;

if p^.right <> nil then begin

dispose_tree(p^.right); end;

384

Глава 4 Типовые алгоритмы обработки информации

____________________________________________________________________

dispose(p); end

end;

procedure obhod(p: PTree); begin

if p <> nil then begin

obhod(p^.left); write(p^.node, ' '); obhod(p^.right);

end;

end; begin

writeln(UTF8ToConsole('введите номер или имя вершины'));

readln(s);

new(current);

root:= current;

current^.node:= s;

current^.left:= nil;

current^.right:= nil;

repeat

writeln;

writeln(UTF8ToConsole('Корень текущего подерева: '),

current^.node);

writeln(UTF8ToConsole('Выберите нужное действие:')); writeln(UTF8ToConsole('1-ввод левого поддерева')); writeln(UTF8ToConsole('2-ввод правого поддерева'));

writeln(UTF8ToConsole('3-сделать корень поддерева текущим'));

writeln(UTF8ToConsole('4-просмотреть дерево'));

385

4.3 Динамические структуры данных

____________________________________________________________________

writeln(UTF8ToConsole('5-удалить текущее поддерево'));

writeln(UTF8ToConsole('6-обход дерева слева'));

writeln(UTF8ToConsole('7-выход из программы')); readln(choose);

case choose of

1: begin {Создание левого поддерева} if current^.left= nil then

new(p) else

p:= current^.left; writeln(UTF8ToConsole('введите номер или имя вершины')); readln(s);

p^.node:= s; p^.left:= nil; p^.right:= nil; current^.left:= p;

end;

2: begin {Создание правого поддерева} if current^.right= nil then

new(p) else

p:= current^.right; writeln(UTF8ToConsole('введите номер или имя вершины')); readln(s);

p^.node:= s; p^.left:= nil; p^.right:= nil; current^.right:= p;

end;

386

Глава 4 Типовые алгоритмы обработки информации

____________________________________________________________________

3: begin {Поиск нужной вершины} writeln(UTF8ToConsole('введите номер или имя вершины')); readln(s);

current_tree:= nil; ptr_tree:= root;

search_node (s, ptr_tree, current_tree); if current_tree <> nil then

current:= current_tree; end;

4: begin {Вывод введенного дерева на экран} ptr_tree:= root; view_tree(ptr_tree);

end;

5: begin {Удаление поддерева} writeln(UTF8ToConsole('введите букву L для')); writeln(UTF8ToConsole('удаления левого поддерева')); writeln(UTF8ToConsole('или любой символ для')); writeln(UTF8ToConsole('удаления правого поддерева')); readln(s);

if (s= 'l') or (s= 'L') then begin {Удаление левого поддерева} ptr_tree:= current^.left;

current^.left:= nil; dispose_tree(ptr_tree);

end else

begin {Удаление правого поддерева} ptr_tree:= current^.right; current^.right:= nil;

387

4.3 Динамические структуры данных

____________________________________________________________________

dispose_tree(ptr_tree); end;

end; 6: begin

writeln(UTF8ToConsole('Обход двоичного дерева слева:')); writeln;

obhod(root);

writeln;

end;

end; { end of case } until choose = 7

end.

4.3.7 Сортировка и поиск с помощью двоичного дерева

Двоичные деревья чаще всего применяются для сортировки и поиска.

Пусть для определенности сортируется массив, состоящий из целых чисел. Для этого сначала строится двоичное дерево по следующему алгоритму: в качестве корня двоичного дерева берется первый элемент массива. Следующие элемен-

ты массива становятся либо левыми, либо правыми потомками в зависимости от значения элемента. Если следующий элемент меньше корня, то он вставля-

ется в левое поддерево, если больше корня, то в правое поддерево, причем вставляется в нужное место в зависимости от значения текущего элемента. По-

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

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

Такие двоичные деревья называются двоичными деревьями поиска или де-

ревья бинарного поиска (binary search tree), поскольку при поиске ис-

пользуется упорядоченность двоичного дерева. Поиск происходит следующим

388

Глава 4 Типовые алгоритмы обработки информации

____________________________________________________________________

образом. В качестве текущего узла принимаем корень. Затем сравниваем значе-

ние искомого элемента со значением текущего узла. Если они равны, то тре-

буемый элемент найден и алгоритм заканчивает свою работу. В противном слу-

чае, если искомый элемент меньше значения текущего узла, то текущим делаем левое поддерево, если больше, то текущим становится правое поддерево. Снова сравниваем наш элемент с текущим узлом и так далее до тех пор, пока искомый элемент не будет найден, либо не будет достигнут пустой узел, что будет озна-

чать, что искомого элемента в массиве нет.

Выше мы уже отмечали, что деревья обычно не вводятся, а формируются программно. Напишем процедуру формирования двоичного дерева по заданно-

му массиву целых чисел:

procedure insert_node(Elem: integer; var root: PTree); var

p: ^Tree; // текущий узел newTree: ^Tree; // новый узел

begin

p:= root; // начинаем с корня и проходим до нужного узла while (Elem > p^.node) and (p^.right <> nil) or (Elem < p^.node) and (p^.left <> nil) do

if Elem < p^.node then p:= p^.left

else

p:= p^.right;

New(newTree); {Создание нового узла} newTree^.left:= nil; newTree^.right:= nil; newTree^.node:= Elem;

{В зависимости от значения Elem новый

389

4.3 Динамические структуры данных

____________________________________________________________________

узел добавляется либо справа, либо слева}

if Elem > p^.node then

p^.right:= newTree

else

p^.left:= newTree;

end;

Процедуру поиска мы уже разрабатывали в последнем примере предыду-

щего раздела. На этот раз оформим поиск в виде функции:

function Search_Elem(Elem: integer;

var root: PTree): boolean;

var

p: PTree; begin

Search_Elem:= false;

if root = nil then exit; p:= root;

if p^.node = Elem then

Search_Elem:= true // элемент найден else

if Elem < p^.node then

Search_Elem:= Search_Elem(Elem, p^.left) else

Search_Elem:= Search_Elem(Elem, p^.right);

end;

Теперь давайте напишем программу сортировки массива целых чисел по возрастанию и поиска в дереве бинарного поиска нужного элемента. Все нуж-

390