Класс BinTree
Класс BinTree содержит одно свойство - объект класса TreeNode, задающий корень дерева и группу операций над элементами дерева. Одним из основных методов класса является метод SearchAndInsert, который, по существу, реализует две операции - поиска элемента в дереве по заданному ключу и вставки элемента в дерево. Заметьте, что вставка должна быть реализована так, чтобы выполнялось основное условие дерева поиска: для каждой вершины ее ключ больше всех ключей вершин левого поддерева и меньше всех ключей вершин правого поддерева. Такая структура обеспечивает эффективное выполнение операций поиска, так как в этом случае для поиска требуется просмотр вершин, лежащих только на одной ветви дерева, ведущей от корня к искомому элементу. Этот метод обеспечивает создание класса, позволяя, начав с корня, вставлять элемент за элементом. Кроме этого метода мы определим методы для удаления элементов и группу методов обхода дерева. Все методы реализованы с использованием рекурсивных алгоритмов. Приведем описание класса:
Пример 9.4.
(html, txt)
Все методы класса довольно подробно прокомментированы, однако хотелось бы подчеркнуть некоторые моменты:
- Начнем с общего замечания, связанного с реализацией рекурсивных алгоритмов. Рекурсия это мощный инструмент, полезный при решении многих задач по обработке данных. Для тех, кто не привык писать рекурсивные программы, мы рекомендуем внимательно разобрать реализацию приведенных методов класса. Каждое рекурсивное определение содержит некоторый базис, позволяющий найти решение в простейшем случае без использования рекурсии, а затем вся задача сводится к нескольким подобным задачам, но меньшей размерности. Если число задач, к которым сводится исходная задача, не меньше двух, то можно заведомо говорить, что рекурсивное решение намного проще не рекурсивного алгоритма и использование рекурсии оправданно. Для пояснения этих общих утверждений обратимся к примеру. В нашем классе приведены три метода обхода бинарного дерева: PrefixOrder, InfixOrder, PostfixOrder.
Написать не рекурсивный алгоритм, который обходил бы все узлы дерева некоторым заданным образом не так то просто. Другое дело рекурсивное определение. Действительно базисное решение очевидно, - когда дерево пусто, то ничего и делать не надо. Если же оно не пусто, то у нас есть корень дерева, а у него два потомка, которые в свою очередь являются деревьями. Поэтому для обхода всего дерева достаточно посетить корень, а затем обойти (рекурсивно) оба поддерева. Меняя порядок посещения корня и поддеревьев, получаем три различных способа обхода дерева. Заметим, именно благодаря тому, что сама структура данных рекурсивна, рекурсивные алгоритмы естественным образом описывают решения задач по обработке таких данных. Рекурсивные определения просты и понятны, но напоминают некоторый фокус. Наиболее сложно воспроизвести вычисления, выполняемые рекурсивным алгоритмом. - Для простоты в методе SearchAndInsert мы совместили две операции поиска элемента по заданному ключу и вставки нового элемента. Если в дереве найден элемент с заданным ключом, то предполагается, что речь идет о поиске и возвращается информация из информационного поля этого элемента. Если в дереве нет элемента с таким ключом, то создается новый узел дерева. Заметьте, что наше решение не позволяет производить замену элемента, а в процессе поиска не уведомляет об отсутствии элемента с заданным ключом
- Удаление элемента из дерева поиска осложняется тем, что нужно поддерживать структуру дерева поиска. В тех случаях, когда нужно удалить элемент, у которого есть два потомка, вызывается специальная процедура ReplaceAndDelete. Эта процедура ищет кандидата, который мог бы заменить удаляемый элемент, сохраняя структуру дерева.
- Недостатком деревьев поиска является то, что они могут быть плохо сбалансированы и могут иметь относительно длинные ветви. Так, если при создании дерева поиска, ключи будут поступать в отсортированном порядке, то дерево будет представлено одной ветвью. Работа с этой структурой данных предполагает, что при создании и добавлении элементов в дерево ключи поступают в случайном порядке, хорошо перемешанные.Эта структура особенно применима в тех случаях, когда в процессе работы над данными широко используются все операции - поиск, вставка и удаление.
- Мы не стали писать реализацию этого класса, оперирующего с данными, хранящимися в списках Excel или базе данных Access, поскольку это выходит за рамки этой лекции.
End Sub
Public Sub ReplaceAndDelete(q As TreeNode) ' Заменяет узел на самый правый If Not (root.right.root Is Nothing) Then root.right.ReplaceAndDelete q Else 'Найден самый правый q.key = root.key: q.info = root.info Set root = root.left.root End If
End Sub
Пример 9.4.
Все методы класса довольно подробно прокомментированы, однако хотелось бы подчеркнуть некоторые моменты:
- Начнем с общего замечания, связанного с реализацией рекурсивных алгоритмов. Рекурсия это мощный инструмент, полезный при решении многих задач по обработке данных. Для тех, кто не привык писать рекурсивные программы, мы рекомендуем внимательно разобрать реализацию приведенных методов класса. Каждое рекурсивное определение содержит некоторый базис, позволяющий найти решение в простейшем случае без использования рекурсии, а затем вся задача сводится к нескольким подобным задачам, но меньшей размерности. Если число задач, к которым сводится исходная задача, не меньше двух, то можно заведомо говорить, что рекурсивное решение намного проще не рекурсивного алгоритма и использование рекурсии оправданно. Для пояснения этих общих утверждений обратимся к примеру. В нашем классе приведены три метода обхода бинарного дерева: PrefixOrder, InfixOrder, PostfixOrder. Написать не рекурсивный алгоритм, который обходил бы все узлы дерева некоторым заданным образом не так то просто. Другое дело рекурсивное определение. Действительно базисное решение очевидно, - когда дерево пусто, то ничего и делать не надо. Если же оно не пусто, то у нас есть корень дерева, а у него два потомка, которые в свою очередь являются деревьями. Поэтому для обхода всего дерева достаточно посетить корень, а затем обойти (рекурсивно) оба поддерева. Меняя порядок посещения корня и поддеревьев, получаем три различных способа обхода дерева. Заметим, именно благодаря тому, что сама структура данных рекурсивна, рекурсивные алгоритмы естественным образом описывают решения задач по обработке таких данных. Рекурсивные определения просты и понятны, но напоминают некоторый фокус.
Наиболее сложно воспроизвести вычисления, выполняемые рекурсивным алгоритмом. - Для простоты в методе SearchAndInsert мы совместили две операции поиска элемента по заданному ключу и вставки нового элемента. Если в дереве найден элемент с заданным ключом, то предполагается, что речь идет о поиске и возвращается информация из информационного поля этого элемента. Если в дереве нет элемента с таким ключом, то создается новый узел дерева. Заметьте, что наше решение не позволяет производить замену элемента, а в процессе поиска не уведомляет об отсутствии элемента с заданным ключом
- Удаление элемента из дерева поиска осложняется тем, что нужно поддерживать структуру дерева поиска. В тех случаях, когда нужно удалить элемент, у которого есть два потомка, вызывается специальная процедура ReplaceAndDelete. Эта процедура ищет кандидата, который мог бы заменить удаляемый элемент, сохраняя структуру дерева.
- Недостатком деревьев поиска является то, что они могут быть плохо сбалансированы и могут иметь относительно длинные ветви. Так, если при создании дерева поиска, ключи будут поступать в отсортированном порядке, то дерево будет представлено одной ветвью. Работа с этой структурой данных предполагает, что при создании и добавлении элементов в дерево ключи поступают в случайном порядке, хорошо перемешанные. Эта структура особенно применима в тех случаях, когда в процессе работы над данными широко используются все операции - поиск, вставка и удаление.
- Мы не стали писать реализацию этого класса, оперирующего с данными, хранящимися в списках Excel или базе данных Access, поскольку это выходит за рамки этой лекции.
Содержание раздела