Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2_SAOD_-_Dinamicheskie_struktury_dannykh.doc
Скачиваний:
121
Добавлен:
21.03.2016
Размер:
1.66 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, если дерево пусто

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

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

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

end; // tBinaryTree

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

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

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

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

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

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

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

var

Item: pItem;

NLeft, NRight: Word;

v: tValue;

begin

ifn <> 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

procedureUpDown(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может быть выполнено с помощью следующего метода:

proceduretBinaryTree.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 возвращает новое (исключенное) поддерево:

functiontBinaryTree.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 помощью отступов

procedureWriteTree(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,обработка узла будет заключаться в увеличении на единицу числа вершин поддерева.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]