Поиск высоты дерева алгоритм

Высота двоичного дерева в C/C++

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

Давайте рассмотрим приведенное ниже двоичное дерево.

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

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

Логика для нахождения высоты двоичного дерева в C++

Давайте теперь решим логику определения высоты и сначала напишем наш псевдокод.

Мы будем использовать рекурсию по дереву, чтобы найти высоту. (Концепции см. в статье Википедии)

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

Теперь мы можем применить определение высоты к поддеревьям.

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

Поскольку высота дерева равна максимальной высоте поддерева + 1, мы продолжаем делать это, пока поддерево не станет NULL , а его высота равна 0.

В этот момент наша функция окончательно завершится, и

Давайте напишем функцию tree_height() , которая вычисляет высоту.

// Find height of a tree, defined by the root node int tree_height(Node* root) < if (root == NULL) return 0; else < // Find the height of left, right subtrees left_height = tree_height(root->left); right_height = tree_height(root->right); // Find max(subtree_height) + 1 to get the height of the tree return max(left_height, right_height) + 1; > 

Строка №3 оценивает условие завершения, когда размер поддерева равен 0 или когда корневой узел имеет значение NULL .

Строки 7 и 8 рекурсивно находят высоту левого и правого поддеревьев.

И, наконец, строка 11 возвращает максимальное значение из двух, возвращая высоту дерева.

Реализация на С/С++

Ниже приведена полная программа, показывающая, как строится двоичное дерево, а затем показана логика определения высоты в tree_height() .

#include #include typedef struct Node Node; // Define the Tree Node here struct Node < int value; // Pointers to the left and right children Node* left, *right; >; Node* init_tree(int data) < // Creates the tree and returns the // root node Node* root = (Node*) malloc (sizeof(Node)); root->left = root->right = NULL; root->value = data; return root; > Node* create_node(int data) < // Creates a new node Node* node = (Node*) malloc (sizeof(Node)); node->value = data; node->left = node->right = NULL; return node; > void free_tree(Node* root) < // Deallocates memory corresponding // to every node in the tree. Node* temp = root; if (!temp) return; free_tree(temp->left); free_tree(temp->right); if (!temp->left && !temp->right) < free(temp); return; >> int tree_height(Node* root) < // Get the height of the tree if (!root) return 0; else < // Find the height of both subtrees // and use the larger one int left_height = tree_height(root->left); int right_height = tree_height(root->right); if (left_height >= right_height) return left_height + 1; else return right_height + 1; > > int main() < // Program to demonstrate finding the height of a Binary Tree // Create the root node having a value of 10 Node* root = init_tree(10); // Insert nodes onto the tree root->left = create_node(20); root->right = create_node(30); root->left->left = create_node(40); root->left->right = create_node(50); // Find the height of the tree int height = tree_height(root); printf("Height of the Binary Tree: %d\n", height); // Free the tree! free_tree(root); return 0; > 

Все права защищены. © Linux-Console.net • 2019-2023

Читайте также:  Ветер сломал дерево на машину

Источник

Поиск в ширину на Python

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

В чем разница?

Когда говорят о поиске в ширину (Breadth First Search, BFS) и глубину (Depth First Search, DFS), имеется в виду порядок обхода узлов двоичного дерева. При обходе в глубину вы сначала опускаетесь к низу дерева, а потом идете в сторону, а при обходе в ширину — наоборот, начинаете с корня и спускаетесь сначала к его узлам-потомкам, обходите их, потом спускаетесь к потомкам потомков, обходите их, и так далее.

Если взять в качестве примера двоичное дерево на этом рисунке, при BFS-подходе порядок обхода узлов будет следующим: 1, 2, 3, 4, 5.

В случае с DFS возможны разные варианты последовательности посещения узлов. Все зависит от того, будет это прямой, обратный или центрированный обход. Например, прямой обход выдаст 1, 2, 4, 5, 3. Мы разбирали эти три типа обхода в статье «Обход двоичного дерева на Python».

Реализация поиска в ширину на Python

Давайте посмотрим, как сделать BFS на Python. Начнем с определения нашего класса TreeNode. В нем должны быть только значение и левый и правый указатели.

class TreeNode: def __init__(self, value): self.value = value self.left = None self.right = None

1. Стратегия

Вот как мы будем обходить узлы:

  1. Найдем высоту дерева от корня до самого дальнего листа.
  2. В цикле переберем все уровни (заканчивая верхушкой).
  3. Обойдем каждый уровень, выводя все узлы.

2. Поиск высоты дерева

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

Да, вы угадали: метод будет рекурсивным, как и большинство методов для обхода двоичных деревьев. Давайте подумаем, каким будет базовый случай. Самый простой вариант — когда у нас нулевой корень. В этом случае высота — 0. Вам может показаться, что простейший случай — узел без потомков, но тогда нам придется проверять, есть ли потомки, а это уже увеличивает сложность.

def height(node): if not node: return 0

Что дальше? Нам нужно обойти левую и правую половину дерева. Мы будем вызывать метод height() для этих половин и сохранять в две переменные: l_height и r_height .

def height(node): if not node: return 0 l_height = height(node.left) r_height = height(node.right)

Какую высоту мы вернем: левую или правую? Конечно, большую! Поэтому мы берем max() обоих значений и возвращаем его. Не забудьте добавить 1 для текущего узла.

def height(node): if not node: return 0 l_height = height(node.left) r_height = height(node.right) return max(l_height, r_height) + 1

3. Перебор уровней в цикле

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

Читайте также:  Навесы из дерева эскизы

Затем нам нужно получить нашу высоту.

def breadth_first(root): h = height(root)

Наконец, мы перебираем в цикле высоту дерева. На каждой итерации мы будем вызывать вспомогательную функцию — print_level() , которая принимает узел root и уровень.

def breadth_first(root): h = height(root) for i in range(h): print_level(root, i)

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

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

def print_level(root, level):

В этом методе номер уровня определяется индексом i цикла for . Чтобы обойти все дерево, мы будем рекурсивно двигаться по уровням, уменьшая i , пока не достигнем уровня 0. Когда достигнем, это будет означать, что теперь можно выводить узлы на экран.

Наш базовый случай — когда корень — null . Этот метод только выводит дерево на экран и ничего не возвращает, поэтому в базовом случае будет просто return .

def print_level(root, level): if not root: return

Далее, если наш уровень — 0 (то есть интересующий нас уровень), нам нужно вывести значение этого узла.

def print_level(root, level): if not root: return if level == 0: print(root.value)

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

def print_level(root, level): if not root: return if level == 0: print(root.value) elif level > 0: print_level(root.left, level - 1) print_level(root.right, level - 1)

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

5. Тестирование

Для начала давайте создадим дерево, используя наш класс TreeNode .

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

tree.left = TreeNode(2) tree.right = TreeNode(3) tree.left.left = TreeNode(4) tree.left.right = TreeNode(5)

Наконец, запускаем наш метод.

Ожидаемый результат: 1, 2, 3, 4, 5 . Мы успешно обошли дерево!

Читайте также:  Масло австралийского чайного дерева lambre

Источник

Найдите высоту бинарного дерева, представленного родительским массивом

Учитывая массив, представляющий отношения родитель-потомок в двоичном дереве, найдите высоту дерева, не строя его. Отношения между родителями и детьми определяются (A[i], i) для каждого индекса i в массиве A . Значение корневого узла будет i если -1 присутствует в индексе i в массиве.

Глубина “узла” — это общее количество ребер от узла до корневого узла дерева. Корень — это единственный узел, глубина которого равна 0.

Высота “узла” — это общее количество ребер на самом длинном пути от узла до листа. Высота “дерева” будет высотой его корневого узла или, что то же самое, глубиной его самого глубокого узла. Листовой узел будет иметь высоту 0.

  • -1 присутствует в индексе 0, что означает, что корень бинарного дерева является узлом 0.
  • 0 присутствует в индексах 1 и 2, что означает, что левый и правый потомки узла 0 — это 1 и 2.
  • 1 присутствует в индексе 3, что означает, что левый или правый дочерний элемент узла 1 равен 3.
  • 2 присутствует в индексах 4 и 5, что означает, что левый и правый потомки узла 2 — это 4 и 5.
  • 4 присутствует в индексах 6 и 7, что означает, что левый и правый потомки узла 4 — это 6 и 7.

Соответствующее бинарное дерево:

Build Binary Tree from Parent Array

Связанный пост:

Простым решением было бы построить двоичное дерево с использованием родительского массива, как показано в сообщении выше. После того, как дерево построено, рассчитайте его высоту. Временная сложность приведенного выше решения равна O(n) и требует O(n) дополнительное пространство, где n размер родительского массива. Однако, согласно ограничениям задачи, высота должна рассчитываться без построения дерева.

Идея состоит в том, чтобы вычислить глубину каждого узла, используя рекурсия и вернуть максимальную найденную глубину. Поскольку глубина узла — это общее количество ребер от узла к корню, нам нужно проследить путь узла к корневому узлу, используя отношение родитель-потомок. Мы можем сделать это, рекурсивно обходя родительский массив. Алгоритм может быть реализован следующим образом на C, Java и Python:

C

результат:

The height of the binary tree is 3

Java

результат:

The height of the binary tree is 3

Python

результат:

The height of the binary tree is 3

Временная сложность приведенного выше решения равна O(n 2 ) , куда n это общее количество узлов в бинарном дереве. Вспомогательное пространство, необходимое программе, равно O(h) для стека вызовов, где h это высота дерева.

Обратите внимание, что findDepth() рутина имеет оптимальное основание поскольку его можно рекурсивно разбить на более мелкие подпрограммы, т.е. findDepth(i) = 1 + findDepth(parent[i]) . Он также демонстрирует перекрывающиеся подзадачи так как одна и та же подзадача решается снова и снова.

Мы знаем, что задачи, имеющие оптимальную подструктуру и перекрывающиеся подзадачи, могут быть решены с помощью динамического программирования. Ниже приведена реализация динамического программирования на C, Java и Python, где решения подзадач памятка, а не вычисляется повторно. Решение использует вспомогательный массив для хранения глубины каждого узла.

Источник

Оцените статью