Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Теория деревья.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
226.82 Кб
Скачать

3.2. Обходы бинарных деревьев

Обойти дерево – это побывать в каждом из его узлов точно по одному разу. Рассмотрим три наиболее часто используемых способов обхода бинарных деревьев – это обход в прямом, симметричном и обратном порядке. Все три обхода будем определять рекурсивно.

а) Прямой обход:

попасть в корень;

пройти левое поддерево данного корня;

пройти правое поддерево данного корня.

Подпрограмму, составляющую список узлов дерева при прохождении его в прямом порядке, можно записать следующим образом:

void preorder (tree *root)

{

if (root)

{

cout<<root->inf<<"\t";

preorder(root->left);

preorder(root->right);

}

}

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

б) Симметричный обход:

пройти левое поддерево данного корня;

попасть в корень;

пройти правое поддерево данного корня.

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

void inorder (tree *root)

{

if (root)

{

inorder(root->left);

cout<<root->inf<<"\t";

inorder(root->right);

}

}

В случае дерева выражений при симметричном обходе получаем инфиксную форму выражений, совпадающую с привычной формой записи выражений.

в) Обратный обход:

пройти левое поддерево данного корня;

пройти правое поддерево данного корня;

попасть в корень.

Подпрограмму, составляющую список узлов дерева при прохождении его в обратном порядке, можно записать следующим образом:

void postorder (tree *root)

{

if (root)

{

postorder(root->left);

postorder(root->right);

cout<<root->inf<<"\t";

}

}

В случае дерева выражений при обратном обходе получаем постфиксную (польскую) форму выражений, в которой оператор следует за левым и правым операндом.

Пример 3. Рассмотрим 3 вида обхода на примере дерева, изображенного на рис.3.3.

При прохождении в прямом порядке список узлов выглядит следующим образом: 10 7 6 3 8 25 18 12 22 31

При прохождении в симметричном порядке список узлов выглядит следующим образом: 3 6 7 8 10 12 18 22 25 31

При прохождении в обратном порядке список узлов выглядит следующим образом: 3 6 8 7 12 22 18 31 25 10

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

Пример 4.Рассмотрим 3 вида обхода для дерева на рисунке 3.2

Прямой обход (префиксная запись арифметического выражения):

-a×b+/cd/ef

Симметричный обход (инфиксная запись арифметического выражения):

a-b×c/d+e/f

Обратный обход (постфиксная запись арифметического выражения):

abcd/ef/+×-

Замечание. Напоминаем, что изложенные виды обходов применимы ко всем типам бинарных деревьев.

3.3. Операции с деревьями бинарного поиска

Построение дерева

Рассмотрим подпрограмму add (int x, tree *&root), которая добавляет новый узел в дерево так, что бы формировалось дерево бинарного поиска. Она имеет два формальных параметра: x – информация, которая записывается в новый узел; root – указатель на текущий узел дерева (вначале на корень исходного дерева).

Новый узел должен быть сформирован либо как корень дерева (если дерево было до этого пустое), либо в виде левого или правого сына сформированного раньше узла дерева, у которого этот сын отсутствует. Определение места для вставки нового узла производится на основе значения указателя root:

  1. если root==NUL, то есть дерево пусто или найдено место для нового узла; то выделяется оперативная память для нового узла root=new tree; в нее помещается содержимое нового узла root->inf=x. Сыновей у этого узла нет, поэтому ссылки на них полагаются пустыми: root ->left= root ->right=NUL;

  2. если root!=NUL, то есть в узле, на который он указывает, есть запись; то производится сравнение значения данного узла (root ->inf) и значения нового узла (x):

    1. если x< root ->inf, то есть новое значение меньше значения данного узла, то поиск места новой записи продолжается по левому поддереву данного узла; для этого производится рекурсивный вызов подпрограммы add: add(x, root ->left);

    2. если x> root ->inf, то есть новое значение больше значения данного узла, то поиск места новой записи продолжается по правому поддереву данного узла; для этого производится рекурсивный вызов подпрограммы add: add(x, root ->right).

Листинг подпрограммы выглядит следующим образом:

void add (int x, tree *&root)

{

if (!root)

{

root=new tree;

root->inf=x;

root->left=root->right=NULL;

}

else if (x<root->inf)

add(x,root->left);

else

if (x>root->inf)

add(x,root->right);

}

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