Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
САОД Part 1.DOC
Скачиваний:
43
Добавлен:
02.11.2018
Размер:
1.68 Mб
Скачать
  1. Реализация бинарного дерева

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

Описание класса tBinaryTree:

type

tValue = Char; // тип элемента дерева - символьный

pItem = ^tItem; // тип указателя на элемент дерева

tItem = record // элемент дерева

Value: tValue; // содержательная часть элемента дерева

Left, Right: pItem; // указатели на левое и правое поддеревья

end; // tItem

tBinaryTree=class // класс - бинарное дерево

protected

fRoot: pItem; // поле - указатель на корень

fSize: Word; // поле - число элементов дерева

public

// Свойство - указатель на корень дерева (доступ по чтению и записи)

property Root: pItem read fRoot write fRoot;

// Свойство - число элементов дерева (доступ по чтению и записи)

property Size: Word read fSize write fSize;

// Построение дерева из n узлов

procedure Build(n: Word;var f: Text);

// Обход дерева сверху вниз

procedure UpDownRevision(var f: Text);

// Обход дерева слева направо

procedure LeftRightRevision(var f: Text);

// Обход дерева снизу вверх

procedure DownUpRevision(var f: Text);

// Включение поддерева SubTree в левую ветвь узла Addr

procedure AddLeft(Addr: pItem; SubTree: tBinaryTree);

// Включение поддерева SubTree в правую ветвь узла Addr

procedure AddRight(Addr: pItem; SubTree: tBinaryTree);

// Исключение поддерева из левой ветви узла с адресом Addr

function DeleteLeft(Addr: pItem): tBinaryTree;

// Исключение поддерева из правой ветви узла с адресом Addr

function DeleteRight(Addr: pItem): tBinaryTree;

function Addr(v: tValue): pItem; // адрес узла со значением v

function Value(Addr: pItem): tValue; // значение узла Addr

function LeftSon(Addr: pItem): pItem; // левый сын узла Addr

function RightSon(Addr: pItem): pItem; // правый сын Addr

function Father(Addr: pItem): pItem; // отец Addr

function Brother(Addr: pItem): pItem; // брат Addr

function IsLeft(Addr: pItem): Boolean; // Addr - левый сын узла

function IsRight(Addr: pItem): Boolean; // Addr - правый сын узла

// Вывод дерева в файл f с помощью отступов

procedure WriteTo(var f: Text);

// Возвращение числа узлов поддерева с корнем Addr

function NodesQuantity(var Addr:PItem): Word;

function Empty: Boolean; // возвращение true, если дерево пусто

procedure Clear; // удаление всех узлов из дерева

constructor Create; // конструктор дерева

destructor Destroy; override; // деструктор дерева

end; // tBinaryTree

  1. Реализация основных операций над бинарным деревом

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

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

Построение бинарного дерева минимальной высоты с числом узлов n предусматривает, что из входного потока (в данном случае – из текстового файла) считываются значения, размещаемые в узлах строящегося дерева.

procedure tBinaryTree.Build(n: Word; var f: Text);

// Построение дерева минимальной высоты из n узлов из файла f

function BuildTree(n: Word): pItem; // рекурсивная функция построения

var

Item: pItem;

NLeft, NRight: Word;

v: tValue;

begin

if n <> 0

then begin

NLeft:= n div 2; NRight:= n - NLeft-1;

Item:= New(pItem);

Read(f, v);

if Eoln(f) then ReadLn(f);

Item^.Value:= v;

Item^.Left:= BuildTree(NLeft);

Item^.Right:= BuildTree(NRight);

BuildTree:= Item; end

else BuildTree:= nil;

end; // function BuildTree

begin

fRoot:= BuildTree(n); fSize:=n;

end; // procedure tBinaryTree.Build

Обход дерева сверху вниз выполняется с помощью процедуры:

procedure tBinaryTree.UpDownRevision(var f: Text);

// Обход бинарного дерева сверху вниз и вывод результатов в файл f

procedure UpDown(Item: pItem); // рекурсивная процедура обхода

begin

if Item <> nil

then begin

Write(f,Item^.Value); // 1. Обработка узла - вывод в файл f

UpDown(Item^.Left); // 2. Обход левого поддерева

UpDown(Item^.Right); // 3. Обход правого поддерева

end;

end; // procedure UpDown

begin

UpDown(fRoot);

Writeln(f);

end; // procedure tBinaryTree.UpDownRevision

В процедурах обхода слева направо (LeftRightRevision) и снизу вверх (DownUpRevision) изменится только последовательность выполнения основных операций (1, 2 и 3) в соответствии со способом обхода.

Включение поддерева в левую ветвь узла с адресом Addr может быть выполнено с помощью следующего метода:

procedure tBinaryTree.AddLeft(Addr: pItem; SubTree: tBinaryTree);

// Включение поддерева SubTree в левую ветвь узла с адресом Addr

begin

if Addr^.Left <> nil

then WriteLn('Левая ветвь узла не пуста. Включение невозможно')

else begin

Addr^.Left:= SubTree.Root;

// увеличение числа узлов базового дерева на число узлов поддерева

Inc(fSize,SubTree.Size);

// поддерево становится пустым

SubTree.Root := nil; SubTree.Size := 0;

end;

end; // procedure tBinaryTree.AddLeft

Метод исключения поддерева из левой ветви узла с адресом Addr возвращает новое (исключенное) поддерево:

function tBinaryTree.DeleteLeft(Addr:pItem):tBinaryTree;

// Исключение и возвращение поддерева из левой ветви узла с адресом Addr

begin

Result := tBinaryTree.Create;

Result.Root := Addr^.Left;

Result.Size := NodesQuantity(Addr^.Left);

Dec(fSize,Result.Size);

Addr^.Left:= nil;

end; // function tBinaryTree.DeleteLeft

Включение и исключение для правой ветви узла выполняются аналогично. Узел с адресом Addr должен присутствовать в дереве.

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

procedure tBinaryTree.WriteTo(var f: Text);

// Вывод дерева в файл f c помощью отступов

procedure WriteTree(Item: pItem; Step: Byte);

// Рекурсивная процедура вывода элемента Item со Step пробелами

begin

if Item <> nil

then begin

WriteTree(Item^.Right, Step+2);

WriteLn(f,' ':Step, Item^.Value);

WriteTree(Item^.Left, Step+2);

end;

end; // procedure WriteTree

begin

WriteTree(fRoot, 5);

end; // procedure tBinaryTree.WriteTo

Остальные операции над бинарным деревом могут быть реализованы на основе уже рассмотренных методов исследования дерева.

Функция Father(Addr), возвращающая указатель на отца узла с адресом Addr, может быть реализована с использованием рекурсивной процедуры обхода дерева сверху вниз, причем обработка узла при этом заключается в сравнении указателей сыновей этого узла с заданным адресом Addr. Если ни один из узлов дерева не имеет сыновей с адресом Addr, то узел с адресом Addr – корень дерева, и функция Father(Addr) должна возвращать значение nil. Напомним, что при выполнении этой и подобных операций предполагается, что узел с указателем Addr присутствует в дереве. При реализации операций эту ситуацию можно анализировать дополнительно.

Функция Brother(Addr), возвращающая указатель на брата узла Addr, может сначала вызывать метод Father(Addr) для определения адреса отца этого узла, а затем сравнивать адреса сыновей найденного предка с адресом Addr и выдавать не совпадающий с Addr адрес сына в качестве результата операции.

Функции IsLeft(Addr) и IsRight(Addr), возвращающие значение «истина», если узел с адресом Addr является соответственно левым или правым сыном некоторого узла дерева, также реализуются путем вызова метода Father(Addr) и последующего анализа адресов его сыновей.

В конструкторе Create указателю на корень дерева fRoot присваивается значение nil и полю fSize – значение 0 .

Процедура удаления всех узлов дерева Clear может быть реализована с использованием рекурсивной процедуры обхода дерева снизу вверх, обработка текущего узла с указателем Item при этом заключается в удалении его из памяти ЭВМ процедурой Dispose(Item). После вызова рекурсивной процедуры обхода указатель корня дерева должен получить значение nil, а поле fSize – значение 0

Деструктор Destroy класса tBinaryTree содержит вызов метода Clear.

Функция NodesQuantity(Addr) вычисления количества узлов поддерева с корнем Addr может быть реализована на основе любой процедуры обхода поддерева, начиная от узла с адресом Addr, обработка узла будет заключаться в увеличении на единицу числа вершин поддерева.