Что такое бинарное дерево поиска bst java

Двоичное дерево поиска (Binary Search Tree (BST))

Свойства, которые отличают двоичное дерево поиска от обычного двоичного дерева:

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

Двоичное дерево справа не является двоичным деревом поиска, потому что правое поддерево узла «3» содержит значение, которое меньше его.

Есть две основные операции, которые вы можете выполнять в двоичном дереве поиска:

1. Проверка, присутствует ли число в двоичном дереве поиска.

Алгоритм зависит от свойства BST так, что, если каждое левое поддерево имеет значения ниже корневого, то каждое правое поддерево имеет значения выше корневого.

Если значение ниже корневого, мы можем с уверенностью сказать, что значение находится не в правом поддереве, а значит нам стоит искать его в левом поддереве.
И наоборот, если значение выше корневого, наверняка, что значение находится не в левом поддереве, а значит нужно искать в правом.

Алгоритм:

Давайте попробуем визуализировать алгоритм с помощью диаграммы.

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

Если значение найдено, мы возвращаем значение, чтобы оно распространялось на каждом шаге рекурсии, как показано на рисунке ниже.

Вы могли заметить, что мы вызывали функцию return search (struct node) четыре раза. Когда мы возвращаем либо новый узел, либо NULL, значение возвращается снова и снова, до тех пор, пока search (root) не вернет окончательный результат.

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

Если значение не найдено, мы в конечном итоге достигаем левого или правого потомка конечного узла, который равен NULL, и он распространяется и возвращается.

2. Вставка значения в двоичное дерево поиска (BST)

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

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

Алгоритм:

Алгоритм не так прост, как кажется. Давайте попробуем визуализировать, как мы добавляем число к существующему BST.

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

Мы подсоединили узел, но нам все еще нужно выйти из функции, не нанося вреда остальной части дерева. Вот где return node (возвратный узел) в конце пригодится. В этом случае NULL, вновь созданный узел возвращается и присоединяется к родительскому узлу, в противном случае тот же самый узел возвращается без каких-либо изменений, пока мы не вернемся к корневому узлу.

Это гарантирует, что когда мы вернемся вверх по дереву, соединения с другими узлами не изменятся.

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

Полный код для вставки и поиска в BST на языке программирования C приведен ниже:

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

Рекомендуем хостинг TIMEWEB

Рекомендуемые статьи по этой тематике

Источник

Бинарные деревья поиска и рекурсия – это просто

Существует множество книг и статей по данной теме. В этой статье я попробую понятно рассказать самое основное.

Бинарное дерево — это иерархическая структура данных, в которой каждый узел имеет значение (оно же является в данном случае и ключом) и ссылки на левого и правого потомка. Узел, находящийся на самом верхнем уровне (не являющийся чьим либо потомком) называется корнем. Узлы, не имеющие потомков (оба потомка которых равны NULL) называются листьями.

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java
Рис. 1 Бинарное дерево

Бинарное дерево поиска — это бинарное дерево, обладающее дополнительными свойствами: значение левого потомка меньше значения родителя, а значение правого потомка больше значения родителя для каждого узла дерева. То есть, данные в бинарном дереве поиска хранятся в отсортированном виде. При каждой операции вставки нового или удаления существующего узла отсортированный порядок дерева сохраняется. При поиске элемента сравнивается искомое значение с корнем. Если искомое больше корня, то поиск продолжается в правом потомке корня, если меньше, то в левом, если равно, то значение найдено и поиск прекращается.

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java
Рис. 2 Бинарное дерево поиска
Сбалансированное бинарное дерево поиска — это бинарное дерево поиска с логарифмической высотой. Данное определение скорее идейное, чем строгое. Строгое определение оперирует разницей глубины самого глубокого и самого неглубокого листа (в AVL-деревьях) или отношением глубины самого глубокого и самого неглубокого листа (в красно-черных деревьях). В сбалансированном бинарном дереве поиска операции поиска, вставки и удаления выполняются за логарифмическое время (так как путь к любому листу от корня не более логарифма). В вырожденном случае несбалансированного бинарного дерева поиска, например, когда в пустое дерево вставлялась отсортированная последовательность, дерево превратится в линейный список, и операции поиска, вставки и удаления будут выполняться за линейное время. Поэтому балансировка дерева крайне важна. Технически балансировка осуществляется поворотами частей дерева при вставке нового элемента, если вставка данного элемента нарушила условие сбалансированности.

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java
Рис. 3 Сбалансированное бинарное дерево поиска

Сбалансированное бинарное дерево поиска применяется, когда необходимо осуществлять быстрый поиск элементов, чередующийся со вставками новых элементов и удалениями существующих. В случае, если набор элементов, хранящийся в структуре данных фиксирован и нет новых вставок и удалений, то массив предпочтительнее. Потому что поиск можно осуществлять алгоритмом бинарного поиска за то же логарифмическое время, но отсутствуют дополнительные издержки по хранению и использованию указателей. Например, в С++ ассоциативные контейнеры set и map представляют собой сбалансированное бинарное дерево поиска.

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java
Рис. 4 Экстремально несбалансированное бинарное дерево поиска

Теперь кратко обсудим рекурсию. Рекурсия в программировании – это вызов функцией самой себя с другими аргументами. В принципе, рекурсивная функция может вызывать сама себя и с теми же самыми аргументами, но в этом случае будет бесконечный цикл рекурсии, который закончится переполнением стека. Внутри любой рекурсивной функции должен быть базовый случай, при котором происходит выход из функции, а также вызов или вызовы самой себя с другими аргументами. Аргументы не просто должны быть другими, а должны приближать вызов функции к базовому случаю. Например, вызов внутри рекурсивной функции расчета факториала должен идти с меньшим по значению аргументом, а вызовы внутри рекурсивной функции обхода дерева должны идти с узлами, находящимися дальше от корня, ближе к листьям. Рекурсия может быть не только прямой (вызов непосредственно себя), но и косвенной. Например А вызывает Б, а Б вызывает А. С помощью рекурсии можно эмулировать итеративный цикл, а также работу структуры данных стек (LIFO).

Кратко обсудим деревья с точки зрения теории графов. Граф – это множество вершин и ребер. Ребро – это связь между двумя вершинами. Количество возможных ребер в графе квадратично зависит от количества вершин (для понимания можно представить турнирную таблицу сыгранных матчей). Дерево – это связный граф без циклов. Связность означает, что из любой вершины в любую другую существует путь по ребрам. Отсутствие циклов означает, что данный путь – единственный. Обход графа – это систематическое посещение всех его вершин по одному разу каждой. Существует два вида обхода графа: 1) поиск в глубину; 2) поиск в ширину.

Поиск в ширину (BFS) идет из начальной вершины, посещает сначала все вершины находящиеся на расстоянии одного ребра от начальной, потом посещает все вершины на расстоянии два ребра от начальной и так далее. Алгоритм поиска в ширину является по своей природе нерекурсивным (итеративным). Для его реализации применяется структура данных очередь (FIFO).

Поиск в глубину (DFS) идет из начальной вершины, посещая еще не посещенные вершины без оглядки на удаленность от начальной вершины. Алгоритм поиска в глубину по своей природе является рекурсивным. Для эмуляции рекурсии в итеративном варианте алгоритма применяется структура данных стек.

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

Асимптотическая сложность обхода и в ширину и в глубину O(V + E), где V – количество вершин, E – количество ребер. То есть является линейной по количеству вершин и ребер. Записи O(V + E) с содержательной точки зрения эквивалентна запись O(max(V,E)), но последняя не принята. То есть, сложность будет определятся максимальным из двух значений. Несмотря на то, что количество ребер квадратично зависит от количества вершин, мы не можем записать сложность как O(E), так как если граф сильно разреженный, то это будет ошибкой.

DFS применяется в алгоритме нахождения компонентов сильной связности в ориентированном графе. BFS применяется для нахождения кратчайшего пути в графе, в алгоритмах рассылки сообщений по сети, в сборщиках мусора, в программе индексирования – пауке поискового движка. И DFS и BFS применяются в алгоритме поиска минимального покрывающего дерева, при проверке циклов в графе, для проверки двудольности.
Обходу в ширину в графе соответствует обход по уровням бинарного дерева. При данном обходе идет посещение узлов по принципу сверху вниз и слева направо. Обходу в глубину в графе соответствуют три вида обходов бинарного дерева: прямой (pre-order), симметричный (in-order) и обратный (post-order).

Прямой обход идет в следующем порядке: корень, левый потомок, правый потомок. Симметричный — левый потомок, корень, правый потомок. Обратный – левый потомок, правый потомок, корень. В коде рекурсивной функции соответствующего обхода сохраняется соответствующий порядок вызовов (порядок строк кода), где вместо корня идет вызов данной рекурсивной функции.

Если нам дано изображение дерева, и нужно найти его обходы, то может помочь следующая техника (см. рис. 5). Обводим дерево огибающей замкнутой кривой (начинаем идти слева вниз и замыкаем справа вверх). Прямому обходу будет соответствовать порядок, в котором огибающая, двигаясь от корня впервые проходит рядом с узлами слева. Для симметричного обхода порядок, в котором огибающая, двигаясь от корня впервые проходит рядом с узлами снизу. Для обратного обхода порядок, в котором огибающая, двигаясь от корня впервые проходит рядом с узлами справа. В коде рекурсивного вызова прямого обхода идет: вызов, левый, правый. Симметричного – левый, вызов, правый. Обратного – левый правый, вызов.

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java
Рис. 5 Вспомогательный рисунок для обходов

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

Надеюсь Вы не уснули, и статья была полезна. Скоро надеюсь последует продолжение банкета статьи.

Источник

Структуры данных: бинарные деревья. Часть 1

Интро

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

В своих статьях я буду приводить примеры кода сразу на двух языках: на Java и на Haskell. Благодаря этому можно будет сравнить императивный и функциональный стили программирования и увидить плюсы и минусы того и другого.

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

Зачем это нужно?

Бинарные деревья поиска обычно применяются для реализации множеств и ассоциативных массивов (например, set и map в с++ или TreeSet и TreeMap в java). Более сложные применения включают в себя ropes (про них я расскажу в одной из следующих статей), различные алгоритмы вычислительной геометрии, в основном в алгоритмах на основе «сканирующей прямой».

В этой статье деревья будут рассмотрены на примере реализации ассоциативного массива. Ассоциативный массив — обобщенный массив, в котором индексы (их обычно называют ключами) могут быть произвольными.

Ну-с, приступим

Двоичное дерево состоит из вершин и связей между ними. Конкретнее, у дерева есть выделенная вершина-корень и у каждой вершины может быть левый и правый сыновья. На словах звучит несколько сложно, но если взглянуть на картинку все становится понятным:

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

У этого дерева корнем будет вершина A. Видно, что у вершины D отсутствует левый сын, у вершины B — правый, а у вершин G, H, F и I — оба. Вершины без сыновей принято называть листьями.

Каждой вершине X можно сопоставить свое дерево, состоящее из вершины, ее сыновей, сыновей ее сыновей, и т.д. Такое дерево называют поддеревом с корнем X. Левым и правым поддеревьями X называют поддеревья с корнями соответственно в левом и правом сыновьях X. Заметим, что такие поддеревья могут оказаться пустыми, если у X нет соответствующего сына.

Данные в дереве хранятся в его вершинах. В программах вершины дерева обычно представляют структурой, хранящей данные и две ссылки на левого и правого сына. Отсутствующие вершины обозначают null или специальным конструктором Leaf:

Как видно из примеров, мы требуем от ключей, чтобы их можно было сравнивать между собой ( Ord a в haskell и T1 implements Comparable в java). Все это не спроста — для того, чтобы дерево было полезным данные должны храниться в нем по каким-то правилам.

Какие же это правила? Все просто: если в вершине X хранится ключ x, то в левом (правом) поддереве должны храниться только ключи меньшие (соответственно большие) чем x. Проиллюстрируем:

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

Что же нам дает такое упорядочевание? То, что мы легко можем отыскать требуемый ключ x в дереве! Просто сравним x со значением в корне. Если они равны, то мы нашли требуемое. Если же x меньше (больше), то он может оказаться только в левом (соответственно правом) поддереве. Пусть например мы ищем в дереве число 17:

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

Функция, получающая значение по ключу:

Добавление в дерево

Теперь попробуем сделать операцию добавления новой пары ключ/значение (a,b). Для этого будем спускаться по дереву как в функции get, пока не найдем вершину с таким же ключем, либо не дойдем до отсутсвующего сына. Если мы нашли вершину с таким же ключем, то просто меняем соответствующее значение. В противно случае легко понять что именно в это место следует вставить новую вершину, чтобы не нарушить порядок. Рассмотрим вставку ключа 42 в дерево на прошлом рисунке:

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

Лирическое отступление о плюсах и минусах функционального подхода

Если внимательно рассмотреть примеры на обоих языках, можно увидеть некоторое различие в поведении функциональной и императивной реализаций: если на java мы просто модифицируем данные и ссылки в имеющихся вершинах, то версия на haskell создает новые вершины вдоль всего пути, пройденного рекурсией. Это связано с тем, что в чисто функциональных языках нельзя делать разрушающие присваивания. Ясно, что это ухудшает производительность и увеличивает потребляемую память. С другой стороны, у такого подхода есть и положительные стороны: отсутствие побочных эффектов сильно облегчает понимание того, как функционирует программа. Более подробно об этом можно прочитать в практически любом учебнике или вводной статье про функциональное программирование.

В этой же статье я хочу обратить внимание на другое следствие функционального подхода: даже после добавления в дерево нового элемента старая версия останется доступной! За счет этого эффекта работают ropes, в том числе и в реализации на императивных языках, позволяя реализовывать строки с асимптотически более быстрыми операциями, чем при традиционном подходе. Про ropes я расскажу в одной из следующих статей.

Вернемся к нашим баранам

Теперь мы подобрались к самой сложной операции в этой статье — удалению ключа x из дерева. Для начала мы, как и раньше, найдем нашу вершину в дереве. Теперь возникает два случая. Случай 1 (удаляем число 5):

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

Видно, что у удаляемой вершины нет правого сына. Тогда мы можем убрать ее и вместо нее вставить левое поддерево, не нарушая упорядоченность:

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

Если же правый сын есть, налицо случай 2 (удаляем снова вершину 5, но из немного другого дерева):

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

Тут так просто не получится — у левого сына может уже быть правый сын. Поступим по-другому: найдем в правом поддереве минимум. Ясно, что его можно найти если начать в правом сыне и идти до упора влево. Т.к у найденного минимума нет левого сына, можно вырезать его по аналогии со случаем 1 и вставить его вместо удалеемой вершины. Из-за того что он был минимальным в правом поддереве, свойство упорядоченности не нарушится:

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

На десерт, пара функций, которые я использовал для тестирования:

Чем же все это полезно?

У читателя возможно возникает вопрос, зачем нужны такие сложности, если можно просто хранить список пар [(ключ, значение)]. Ответ прост — операции с деревом работают быстрее. При реализации списком все функции требуют O(n) действий, где n — размер структуры. (Запись O(f(n)) грубо говоря означает «пропорционально f(n)», более корректное описание и подробности можно почитать тут). Операции с деревом же работают за O(h), где h — максимальная глубина дерева (глубина — расстояние от корня до вершины). В оптимальном случае, когда глубина всех листьев одинакова, в дереве будет n=2^h вершин. Значит, сложность операций в деревьях, близких к оптимуму будет O(log(n)). К сожалению, в худшем случае дерево может выродится и сложность операций будет как у списка, например в таком дереве (получится, если вставлять числа 1..n по порядку):

Что такое бинарное дерево поиска bst java. Смотреть фото Что такое бинарное дерево поиска bst java. Смотреть картинку Что такое бинарное дерево поиска bst java. Картинка про Что такое бинарное дерево поиска bst java. Фото Что такое бинарное дерево поиска bst java

К счастью, существуют способы реализовать дерево так, чтобы оптимальная глубина дерева сохранялась при любой последовательности операций. Такие деревья называют сбалансированными. К ним например относятся красно-черные деревья, AVL-деревья, splay-деревья, и т.д.

Анонс следующих серий

В следующей статье я сделаю небольшой обзор различных сбалансированных деревьев, их плюсы и минусы. В следующих статьях я расскажу о каком-нибудь (возможно нескольких) более подробно и с реализацией. После этого я расскажу о реализации ropes и других возможных расширениях и применениях сбалансированных деревьев.

Оставайтесь на связи!

Полезные ссылки

Исходники примеров целиком:

Также очень советую почитать книгу Кормен Т., Лейзерсон Ч., Ривест Р.: «Алгоритмы: построение и анализ», которая является прекрасным учебником по алгоритмам и структурам данных

Источник

Реализация двоичного дерева в Java

Взгляните на реализацию отсортированного двоичного дерева в Java.

1. введение

В этой статье мы рассмотрим реализацию двоичного дерева в Java.

Дальнейшее чтение:

Как распечатать двоичную древовидную диаграмму

Реверсирование двоичного дерева в Java

Глубина первого поиска на Java

2. Двоичное дерево

Двоичное дерево-это рекурсивная структура данных, в которой каждый узел может иметь не более 2 дочерних элементов.

Вот краткое визуальное представление этого типа двоичного дерева:

Затем давайте добавим начальный узел нашего дерева, обычно называемый root:

3. Общие операции

Теперь давайте рассмотрим наиболее распространенные операции, которые мы можем выполнять с двоичным деревом.

3.1. Вставка Элементов

Первая операция, которую мы рассмотрим, – это вставка новых узлов.

Во-первых, мы создадим рекурсивный метод для вставки:

Затем мы создадим открытый метод, который запускает рекурсию из корневого узла:

Теперь давайте посмотрим, как мы можем использовать этот метод для создания дерева из нашего примера:

3.2. Нахождение элемента

Теперь давайте добавим метод, чтобы проверить, содержит ли дерево определенное значение.

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

Здесь мы ищем значение, сравнивая его со значением в текущем узле, а затем продолжаем в левом или правом дочернем узле в зависимости от этого.

Далее, давайте создадим открытый метод, который начинается с root :

Теперь давайте создадим простой тест, чтобы убедиться, что дерево действительно содержит вставленные элементы:

Все добавленные узлы должны содержаться в дереве.

3.3. Удаление элемента

Другой распространенной операцией является удаление узла из дерева.

Во-первых, мы должны найти узел для удаления таким же образом, как и раньше:

Как только мы найдем узел для удаления, есть 3 основных различных случая:

Давайте посмотрим, как мы можем реализовать первый случай, когда узел является конечным узлом:

Теперь давайте продолжим случай, когда у узла есть один дочерний элемент:

Здесь мы возвращаем ненулевой дочерний элемент, чтобы он мог быть назначен родительскому узлу.

Наконец, мы должны рассмотреть случай, когда у узла есть два дочерних элемента.

Во-первых, нам нужно найти узел, который заменит удаленный узел. Мы будем использовать наименьший узел узла для удаления правого поддерева:

Затем мы присваиваем узлу наименьшее значение для удаления, а затем удаляем его из правого поддерева:

Наконец, давайте создадим общедоступный метод, который запускает удаление из root :

Теперь давайте проверим, что удаление работает должным образом:

4. Обход дерева

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

Мы будем использовать то же дерево, что и раньше, и покажем порядок обхода для каждого случая.

4.1. Поиск по Глубине

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

Существует несколько способов выполнить поиск по глубине: по заказу, по предварительному заказу и после заказа.

Обход по порядку состоит из первого посещения левого поддерева, затем корневого узла и, наконец, правого поддерева:

Если мы вызовем этот метод, вывод консоли покажет обход по порядку:

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

И давайте проверим обход предварительного заказа в выводе консоли:

Обход после заказа посещает левое поддерево, правое поддерево и корневой узел в конце:

Вот узлы в пост-порядке:

4.2. Поиск по Ширине

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

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

В этом случае порядок узлов будет:

5. Заключение

В этой статье мы рассмотрели, как реализовать отсортированное двоичное дерево в Java и его наиболее распространенные операции.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *