Деревья. Виды деревьев

Содержание

Слайд 2

Примеры деревьев Генеалогические деревья и организационные диаграммы. Деревья используются при анализе

Примеры деревьев

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

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

Будем определять дерево как конечное множество Т, состоящее из одного или

Будем определять дерево как конечное множество Т, состоящее из одного или

более узлов, таких, что:

имеется один специально обозначенный узел, называемый корнем данного дерева;
остальные узлы (исключая корень) содержатся в попарно не пересекающихся множествах Т1 , Т2 , … , Тn , каждое из которых в свою очередь является деревом. Деревья Т1 , Т2 , … , Тn называются поддеревьями данного корня.
Это определение является рекурсивным, т.е. мы определили дерево в терминах самих же деревьев.

Слайд 4

Каждый узел дерева является корнем некоторого поддерева, которое содержится в этом

Каждый узел дерева является корнем некоторого поддерева, которое содержится в этом

дереве. Число поддеревьев данного узла называется степенью этого узла. Узел с нулевой степенью называется листом.

Дерево на рисунке имеет корень A и 5 листьев: H, J, D, G, F. Степени вершин этого дерева следующие: A, В имеют степень 2, С – 3, Е – 1. Корень А располагается на 1 уровне, узлы В, С – на втором, H, J, D, E, F – на третьем, G – на четвертом.

Рис.1

Слайд 5

Бинарное дерево – это дерево, в котором каждый узел имеет не

Бинарное дерево – это дерево, в котором каждый узел имеет не

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

Примеры Дерево двоичного поиска Однако условие идеального сбалансирования для этого дерева

Примеры

Дерево двоичного поиска

Однако условие идеального сбалансирования для этого дерева не выполняется:

для узла со значением 25 количество узлов в левом поддереве равно 3, а в правом поддереве – 1.

Дерево бинарного поиска и идеально сбалансированное дерево одновременно

Рис.2

Рис.3

Слайд 7

На C++ бинарное дерево можно представить следующим образом: struct tree {

На C++ бинарное дерево можно представить следующим образом:
struct tree
{
int inf;


tree *left, *right;
};
tree *root;
В таком представлении дерево определяется указателем на свой корень. Каждый узел содержит информационную часть (inf) и указатели на узлы, которые являются его левым (left) и правым (right) сыном.
Слайд 8

Обходы бинарных деревьев Обойти дерево – это побывать в каждом из

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

Обойти дерево – это побывать в каждом из его

узлов точно по одному разу. Рассмотрим три наиболее часто используемых способов обхода бинарных деревьев – это обход в прямом, симметричном и обратном порядке. Все три обхода будем определять рекурсивно.
а) Прямой обход:
попасть в корень;
пройти левое поддерево данного корня;
пройти правое поддерево данного корня.
Подпрограмму, составляющую список узлов дерева при прохождении его в прямом порядке, можно записать следующим образом:
void preorder (tree *root)
{
if (root)
{
cout<inf<<"\t";
preorder(root->left);
preorder(root->right);
}
}
Слайд 9

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

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

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

данного корня.
Подпрограмму, составляющую список узлов дерева при прохождении его в симметричном порядке, можно записать следующим образом:
void inorder (tree *root)
{
if (root)
{
inorder(root->left);
cout<inf<<"\t";
inorder(root->right);
}
}
Слайд 10

в) Обратный обход: пройти левое поддерево данного корня; пройти правое поддерево

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

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

в корень.
Подпрограмму, составляющую список узлов дерева при прохождении его в обратном порядке, можно записать следующим образом:
void postorder (tree *root)
{
if (root)
{
postorder(root->left);
postorder(root->right);
cout<inf<<"\t";
}
}
Замечание. Таким образом, при симметричном обходе дерева бинарного поиска на экран выводится упорядоченная по возрастанию последовательность данных. Этот свойство дерева бинарного поиска можно использовать для сортировки данных.

Рассмотрим 3 вида обхода на примере дерева, изображенного на рис.2
При прохождении в прямом порядке список узлов выглядит следующим образом: 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

Слайд 11

Операции с деревьями бинарного поиска: Построение дерева Рассмотрим подпрограмму add (int

Операции с деревьями бинарного поиска: Построение дерева

Рассмотрим подпрограмму
add (int x,

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

Построение дерева бинарного поиска void add (int x, tree *&root) {

Построение дерева бинарного поиска

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);
}
Для формирования дерева в основной программе можно написать обращение к этой подпрограмме на этапе ввода в цикле узлы дерева с клавиатуры или считывания их из файла.

Если мы будем вводить с клавиатуры узлы 10, 7, 25, 31, 18, 6, 3, 12, 22, 8, то получим дерево, представленное на рисунке.

Слайд 13

Поиск по дереву Рассмотрим подпрограмму search (int x, tree *root), предназначенную

Поиск по дереву

Рассмотрим подпрограмму
search (int x, tree *root), предназначенную для

поиска и вывода данного узла. Подпрограмма имеет два формальных параметра: x – значение, которое нужно найти; root – указатель на анализируемый узел (вначале root указывает на корень дерева).
Слайд 14

Поиск по дереву void search (int x, tree *root) { if

Поиск по дереву

void search (int x, tree *root)
{
if (!root) cout<<"error";
else

if (x < root->inf) search(x, root->left);
else if (x > root->inf) search(x, root->right);
else cout<<"search is successful";
}
Слайд 15

Операции с идеально сбалансированными деревьями Построение дерева Рассмотрим подпрограмму create (int

Операции с идеально сбалансированными деревьями Построение дерева

Рассмотрим подпрограмму create (int

number, tree *&root), которая используется для формирования идеально сбалансированного дерева.
number – количество узлов в формируемом дереве;
root – указатель на корень дерева.
Необходимо выполнить требование:
для каждого узла количество узлов в левом поддереве отличается от количества узлов в правом поддереве не больше чем на единицу.
Первый узел полагается корнем формируемого дерева, после чего определяется количество узлов в левом поддереве numberLeft = number/2, количество узлов в правом поддереве numberRight = number- numberLeft -1.
Слайд 16

Построение идеально сбалансированного дерева void create (int number, tree *&root) {

Построение идеально сбалансированного дерева

void create (int number, tree *&root)
{
int a;
if (number

> 0)
{
root = new tree;
in >> a; //считываем из файла in очередное число
root->inf = a;
root->left = root->right = NULL;
int numberLeft = number/2, numberRight = number – numberLeft - 1;
create(numberLeft, root->left);
create(numberRight, root->right); }
}