
Мансуров. Основы программирования в среде Lazarus. 2010
.pdfГлава 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