- •Операторы динамического распределения памяти
- •Перегрузка функций и операций
- •Объекты и классы Класс как обобщение структуры
- •Определение первичного класса
- •Перегрузка операций
- •Конструкторы
- •Список инициализации
- •Деструктор
- •Дружественные классы
- •Статические элементы класса
- •Шаблоны функций
- •Контейнерные классы Шаблоны классов
- •Параметризованные очереди и стеки
- •Бинарные деревья
- •Определение класса множества
- •Производные классы Доступ к полям и функциям базового класса
- •Класс дерева поиска
- •Параметризованный связный список
- •Множественное наследование
- •Виртуальные классы
- •Виртуальные функции Переопределение составной функции
- •Организация списка объектов различного типа
- •Виртуальные деструкторы
- •Абстрактные классы
Бинарные деревья
Для определения параметризованного класса бинарного дерева будет использоваться следующий шаблон:
template <class DataT>
class tree
{
DataT info; // информ. часть
tree *left, *right; // указатели на поддеревья
public:
tree <DataT> *root; //корень дерева
tree() { root = NULL;} //конструктор
//добавление элемента в дерево
void stree(tree <DataT>*,tree <DataT>*,DataT);
//удаление из дерева
tree <DataT> * dtree(tree <DataT>*r, DataT key);
void preorder(tree <DataT>*r); // обход прямой
void inorder(tree <DataT>*r); // симметричный обход
void postorder(tree <DataT>*r); // обратный обход
void print_tree(tree <DataT>*r, int l); // вывод дерева
//поиск в дереве
tree <DataT> * search_tree(tree <DataT>*r, DataT key);
};
Для включения новых элементов в дерево используется функция, описанная в теле класса как stree:
//параметризованная функция добавления элемента в дерево
template <class DataT>
void tree <DataT>::stree (tree <DataT> *r, tree <DataT> *previous, DataT info)
{
if (!r)
{
//если в качестве дерева для вставки передан NULL то
r = new tree <DataT>; //выделим память
if (!r)
{
cout<< "\nНедостаточно памяти"; exit(1);
}
//создаётся новое дараво
r -> left = r -> right = NULL;//левое и правое поддеревья пусты
r -> info = info;
if (!root) root = r; // корень
else //если корень первоначального дерева не пуст
{
//вставляем элемент на его место в дереве поиска
if (info<previous -> info) previous -> left = r;
else previous -> right = r;
}
return;
}
//если передано в качастве формального параметра не пустое дерево,
//то вставим элемент либо
if (info < r -> info)
stree (r -> left, r, info);//в левое поддерево
else
stree (r -> right, r, info);//либо в правое
}
Для прохождения дерева с выводом на экран значений его узлов можно использовать функцию обхода в симметричном порядке, описанную в теле класса как inorder:
//параметризованная функция обхода дерева в симметричном порядке
template <class DataT>
void tree <DataT>::inorder(tree <DataT> *r)
{
if(!r) return; //если дерево пусто
inorder (r -> left); //посетим левое поддерево
if(r -> info) cout << r -> info << " "; //вывод элемента
inorder (r -> right); //посетим правое поддерево
}
Эту функцию следует использовать, применяя в качестве аргумента указатель на корень созданного дерева. Значения элементов будут выведены в неубывающем порядке.
Приведем соответствующие функции для обхода дерева в прямом и обратном порядках, описанные в теле класса как preorder и postorder соответственно:
//параметризованная функция обхода дерева в прямом порядке
template <class DataT>
void tree <DataT>::preorder(tree <DataT>*r)
{
if(!r) return; //если дерево пусто
if(r -> info) cout << r -> info << " "; //вывод элемента
preorder (r -> left); //посетим левое поддерево
preorder (r -> right); //посетим правое поддерево
}
//параметризованная функция обхода дерева в обратном порядке
template <class DataT>
void tree <DataT>::postorder(tree <DataT>*r)
{
if(!r) return; //если дерево пусто
postorder (r -> left); //посетим левое поддерево
postorder (r -> right); //посетим правое поддерево
if (r -> info) cout << r -> info << " ";//вывод элемента
}
Для вывода дерева на экран, можно применить следующую подпрограмму, описанную в теле класса как print_tree:
//параметризированная функция печати дерева на экране
template <class DataT>
void tree <DataT>::print_tree(tree <DataT>*r, int l)
{
int i;
if(!r) return; //если дерево пусто
print_tree(r -> right, l+1); //распечатаем правое поддерево на
//l+1 уровне
for (i = 0; i < l; i++) cout << " ";//вывод необходимого
//количества пробелов
cout << r -> info << endl; //вывод информационной части
print_tree(r -> left, l+1); //распечатаем правое поддерево на
//l+1 уровне
}
Для полноты приведём подпрограммы поиска элемента (search_tree) и удаления (dtree) элемента бинарного дерева.
//параметризованная функция поиска поддерева с корнем, равным key
template <class DataT>
tree <DataT> *tree<DataT>::search_tree(tree <DataT>*r, DataT key)
{
if (!r) return r; // если дерево пусто
while (r -> info != key) //цикл пока не найдено поддерево
{
if (key < r -> info) r = r -> left;//ищем слева
else r = r -> right; //ищем справа
if (r == NULL ) break; //если не нашли
}
return r;
}
Если элемент не найден, то эта подпрограмма возвратит NULL.
Подпрограмма удаления элемента реализуется рекурсивно:
//параметризованная функция получения нового дерева из имеющегося
//путём удаления некоторого узла
template <class DataT>
tree <DataT>* tree <DataT>::dtree(tree <DataT> *r, DataT key)
{
tree <DataT> *p;
tree <DataT> *p2; // r - корень дерева
if (!r) return r; // если элемент не найден
if (r -> info == key) //элемент это корень-?
{
if (r -> left == r -> right) // если 1 элемент
{ //вернём пустое дерево
if (root==r) root = NULL;
return NULL; // пустое дерево
}
else if(r -> left == NULL) //если левое поддерево пусто
{
p = r -> right; // нет левого поддерева
if (root == r) root = p;
return p;
}
else if (r -> right == NULL) //если правое поддерево пусто
{
p = r -> left; // нет правого поддерева
if (r == root) root = p;
return p;
}
else //в противном случае
{
p2 = r -> right;//как минимум дерево из правого поддерева
p = r -> right; //правые поддеревья
while (p -> left) p = p -> left;
p -> left = r -> left;
if (r == root) root = p2;
return p2; //вернём новое дерево
}
}
//если не корень, двигаемся
if (r -> info < key) r -> right = dtree(r -> right, key); //вправо
else r -> left = dtree (r -> left, key); //и влево
//пока искомый элемент не станет корнем
return r;
}
Ниже приведём пример программы, использующей вышеописанные функции для работы с бинарным деревом, информационной частью элементов которого являются символы, вводимые пользователем с клавиатуры.
#include <iostream.h> //библиотека потокового ввода-вывода
#include <conio.h> //библиотека консольного ввода-вывода
#include <process.h> //необходимо для использования функции exit
template <class DataT>
class tree
{
DataT info; // информ. часть
tree *left, *right; // указатели на поддеревья
public:
tree <DataT> *root; //корень дерева
tree() { root = NULL;} //конструктор
//добавление элемента в дерево
void stree(tree <DataT>*,tree <DataT>*,DataT);
//удаление из дерева
tree <DataT> * dtree(tree <DataT>*r, DataT key);
void preorder(tree <DataT>*r); // обход прямой
void inorder(tree <DataT>*r); // симметричный обход
void postorder(tree <DataT>*r); // обратный обход
void print_tree(tree <DataT>*r, int l); // вывод дерева
//поиск в дереве
tree <DataT> * search_tree(tree <DataT>*r, DataT key);
};
//параметризованная функция добавления элемента в дерево
template <class DataT>
void tree <DataT>::stree (tree <DataT> *r, tree <DataT> *previous, DataT info)
{
if (!r)
{
//если в качестве дерева для вставки передан NULL то
r = new tree <DataT>; //выделим память
if (!r)
{
cout<< "\nНедостаточно памяти"; exit(1);
}
//создаётся новое дараво
r -> left = r -> right = NULL;//левое и правое поддеревья пусты
r -> info = info;
if (!root) root = r; // корень
else //если корень первоначального дерева не пуст
{
//вставляем элемент на его место в дереве поиска
if (info<previous -> info) previous -> left = r;
else previous -> right = r;
}
return;
}
//если передано в качестве формального параметра не пустое дерево,
//то вставим элемент либо
if (info < r -> info)
stree (r -> left, r, info);//в левое поддерево,
else
stree (r -> right, r, info);//либо в правое
}
//параметризованная функция обхода дерева в симметричном порядке
template <class DataT>
void tree <DataT>::inorder(tree <DataT> *r)
{
if(!r) return; //если дерево пусто
inorder (r -> left); //посетим левое поддерево
if(r -> info) cout << r -> info << " "; //вывод элемента
inorder (r -> right); //посетим правое поддерево
}
//параметризованная функция обхода дерева в прямом порядке
template <class DataT>
void tree <DataT>::preorder(tree <DataT>*r)
{
if(!r) return; //если дерево пусто
if(r -> info) cout << r -> info << " "; //вывод элемента
preorder (r -> left); //посетим левое поддерево
preorder (r -> right); //посетим правое поддерево
}
//параметризованная функция обхода дерева в обратном порядке
template <class DataT>
void tree <DataT>::postorder(tree <DataT>*r)
{
if(!r) return; //если дерево пусто
postorder (r -> left); //посетим левое поддерево
postorder (r -> right); //посетим правое поддерево
if (r -> info) cout << r -> info << " ";//вывод элемента
}
//параметризованная функция печати дерева на экране
template <class DataT>
void tree <DataT>::print_tree(tree <DataT>*r, int l)
{
int i;
if(!r) return; //если дерево пусто
print_tree(r -> right, l+1); //распечатаем правое поддерево на
//l+1 уровне
for (i = 0; i < l; i++) cout << " ";//вывод необходимого
//количества пробелов
cout << r -> info << endl; //вывод информационной части
print_tree(r -> left, l+1); //распечатаем правое поддерево на
//l+1 уровне
}
//параметризованная функция поиска поддерева с корнем, равным key
template <class DataT>
tree <DataT> *tree<DataT>::search_tree(tree <DataT>*r, DataT key)
{
if (!r) return r; // если дерево пусто
while (r -> info != key) //цикл пока не найдено поддерево
{
if (key < r -> info) r = r -> left;//ищем слева
else r = r -> right; //ищем справа
if (r == NULL ) break; //если не нашли
}
return r;
}
//параметризованная функция получения нового дерева из имеющегося
//путём удаления некоторого узла
template <class DataT>
tree <DataT>* tree <DataT>::dtree(tree <DataT> *r, DataT key)
{
tree <DataT> *p;
tree <DataT> *p2; // r - корень дерева
if (!r) return r; // если элемент не найден
if (r -> info == key) //элемент это корень-?
{
if (r -> left == r -> right) // если 1 элемент
{ //вернём пустое дерево
if (root==r) root = NULL;
return NULL; // пустое дерево
}
else if(r -> left == NULL) //если левое поддерево пусто
{
p = r -> right; // нет левого поддерева
if (root == r) root = p;
return p;
}
else if (r -> right == NULL) //если правое поддерево пусто
{
p = r -> left; // нет правого поддерева
if (r == root) root = p;
return p;
}
else //в противном случае
{
p2 = r -> right;//как минимум дерево из правого поддерева
p = r -> right; //правые поддеревья
while (p -> left) p = p -> left;
p -> left = r -> left;
if (r == root) root = p2;
return p2; //вернём новое дерево
}
}
//если не корень, двигаемся
if (r -> info < key) r -> right = dtree(r -> right, key); //вправо
else r -> left = dtree (r -> left, key); //и влево
//пока искомый элемент не станет корнем
return r;
}
int main(void)
{
char stemp[80]; //символьная строка
int i=0; //счётчик
tree <char> ch; //дерево
tree <char> *ch1; //указатель на дерево
ch1=new tree <char>; //выделим память для дерева
clrscr(); //очистим экран
cout << "Введите строку (она должна завершаться точкой):";
cin >> stemp;
do
{
//пока не встретилась точка, вставляем каждый элемент строки
//в дерево ch
if (stemp[i]!='.') ch.stree(ch.root, NULL, stemp[i]);
i++;
}
while (stemp[i] != '.');
cout <<"Обход первоначального дерева в прямом порядке:\n";
ch.preorder(ch.root);
cout <<'\n';
cout <<"Обход первоначального дерева в обратном порядке:\n";
ch.postorder(ch.root);
cout <<'\n';
cout <<"Обход первоначального дерева в симметричном порядке:\n";
ch.inorder(ch.root);
ch1->root=ch.dtree(ch.root,stemp[0]); //получение нового дерева
cout <<'\n';
cout <<"Обход дерева в прямом порядке после удаления первого введённого элемента:\n";
ch1->preorder(ch1->root);
cout <<'\n';
cout <<"Печать окончательного вида дерева:\n";
ch.print_tree(ch.root,0);
return 0;
}
Результат работы программы
Введите строку (она должна завершаться точкой):123321453754.
Обход первоначального дерева в прямом порядке:
1 2 1 3 2 3 4 3 5 4 7 5
Обход первоначального дерева в обратном порядке:
1 2 3 4 5 7 5 4 3 3 2 1
Обход первоначального дерева в симметричном порядке:
1 1 2 2 3 3 3 4 4 5 5 7
Обход дерева в прямом порядке после удаления первого введённого элемента:
2 1 3 2 3 4 3 5 4 7 5
Печать окончательного вида дерева:
7
5
5
4
4
3
3
3
2
2
1