- •Бинарные деревья
- •Проектирование класса TreeNode
- •Разработка функций класса TreeNode
- •Рекурсивные методы прохождения деревьев
- •Симметричный метод прохождения дерева
- •Обратный метод прохождения дерева
- •Удаление дерева
- •Бинарные деревья поиска
- •Ключ в узле бинарного дерева поиска
- •Работа с бинарным деревом поиска
- •Класс BinSTree
- •Пример 2.
- •Симметричный метод прохождения
- •Дублированные узлы
- •Реализация класса BinSTree
- •Практическая задача: конкорданс
- •Более сложные нелинейные структуры
- •Турнирная сортировка
- •Пирамиды
- •Пример3.
- •Пирамидальная сортировка
Проектирование класса TreeNode
Рассмотрим подробно разработку класса TreeNode – «узел бинарного дерева». В состав узла входит поле данных, которое является открытым (public) элементом, то есть к нему можно обращаться непосредственно. Это позволяет читать или обновлять данные во время прохождения дерева, а также допускает возвращение ссылки на данные. Последняя особенность используется более сложными структурами данных, например, словарями. Указатели на правое и левое поддеревья являются закрытыми (private) элементами, доступ к которым осуществляется посредством функций Left() и Right().
//Предварительное описание класса «бинарное дерево поиска»
template <class T>
class BinSTree;
//Определение класса «узел бинарного дерева»
template <class T>
class TreeNode
{
//Указатели на левый и правый порожденные узлы
TreeNode<T> *left;
TreeNode<T> *right;
public:
//Открытый элемент данных класса, допускающий обновление
T data;
//Конструктор: инициализирует поле данных и указатели
//на порожденные узлы
TreeNode (const T& item, TreeNode<T> *lptr, TreeNode<T> *rptr):
data(item), left(lptr), right(rptr) { }
// Методы доступа к указателям на порожденные узлы
TreeNode<T>* Left (void) const;
TreeNode<T>* Right (void) const;
// Описание класса BinSTree как дружественного
friend class BinSTree<T>;
};
Конструктор инициализирует поле данных и указатели на узлы следующего уровня. Если оба указателя инициализировать значением NULL, то узел будет инициализирован как лист.
Если в параметр lptr или rptr конструктора поместить указатели на другой объект TreeNode, конструктор присоединяет этот объект как левый или правый порожденный узел.
Методы доступа Left и Right возвращают соответствующий указатель. Класс BinSTree объявляется дружественным классу TreeNode и может модифицировать указатели. Другие клиенты должны использовать конструктор для создания указателей и методы Left и Right для прохождения дерева.
Пример 1.
//Указатели целочисленных узлов дерева
TreeNode<int> *root, *lchild, *rchild;
TreeNode<int> *p;
//Создать листья, содержащие 20 и 30 в качестве данных
lchild = new TreeNode<int> (20);
rchild = new TreeNode<int> (30);
//Создать корень, содержащий число 10 и двух сыновей
root = new TreeNode<int> (10, lchild, rchild);
root->data = 50; //Присвоить корню 50
Методы Left и Right возвращают значения полей левого и правого указателей. Благодаря этому клиент имеет доступ к левому и правому сыновьям узла.
Построение бинарного дерева
Бинарное дерево состоит из конечного множества объектов TreeNode, связанных между собой посредством полей Left и Right. Объект TreeNode создается динамически с помощью функции new.
TreeNode<int> *p; //Объявление указателя на узел дерева
p = new TreeNode(item);//Левый и правый указатели равны NULL
Вызов функции new обязательно должен включать значение данных, и необязательно указатели на правое и/или левое поддерево.
Определим функцию GetTreeNode, принимающую данные, и ноль или более указателей на объект TreeNode для создания и инициализации узла бинарного дерева. При недостаточном количестве доступной памяти программа прекращается сразу после выдачи сообщения об ошибке.
//Создать объект TreeNode с указательными полями lptr и rptr.
//По умолчанию указатели содержат NULL.
template <class T>
TreeNode<T> *GetTreeNode(T item, TreeNode<T> *lptr = NULL,
TreeNode<T> *rptr = NULL)
{
TreeNode<T> *p;
//Вызвать new для создания нового узла
//Передать туда параметры lptr и rptr
p = new TreeNode<T> (item, lptr, rptr);
//Если памяти недостаточно, завершить
//программу сообщением об ошибке
if (p == NULL)
{
cerr << “Ошибка при выделении памяти!\n";
exit(1);
}
//Вернуть указатель на выделенную системой память
return p;
}
Функция FreeTreeNode принимает указатель на объект TreeNode и освобождает занимаемую узлом память, вызывая оператор С++ delete.
//Освободить динамическую память, занимаемую данным узлом
template <class t>
void FreeTreeNode(TreeNode<T> *p)
{
delete p;
}
Функция GetTreeNode может быть использована для явного построения каждого узла дерева и, следовательно, всего дерева. Это было продемонстрировано на дереве с тремя узлами, содержащими 10, 20 и 30. Для более крупного экземпляра процесс будет немного утомительным, так как вы должны поместить в дерево все значения данных и указателей.
Создадим функцию MakeCharTree, строящую три дерева, узлы которых содержат символьные элементы данных. Эти деревья будут использоваться для иллюстрации методов TreeNode в следующем разделе. Параметры функции включают в себя ссылку на корень дерева и число n (0 < n < 2), которое служит для обозначения дерева. Следующие объявления создают указатель на объект TreeNode, с именем root, и назначают его корнем дерева Tree_2.
//Объявить указатель на корень
TreeNode<char> *root;
//Сформировать на этом корне дерево tree_2
MakeCharTree(root,2);
На рисунке 9 показаны три дерева, построенных этим методом. Мы не приводим здесь кода этой функции ввиду ее примитивности, потренируйтесь сами.
Рис. 9. Дерево MakeCharTree