Дерево — это направленный граф без циклов между узлами. Каждый узел может иметь потомков и родителя. Узел без родителя называется корнем дерева и является начальным. Вершины, у которых нет потомков, называются листьями.
Бинарное или двоичное дерево — это дерево, в котором у каждого узла может быть не более двух потомков.
Бинарное дерево поиска — это бинарное дерево, в котором в левом поддереве значения меньше, чем само значение
родителя, а в правом поддереве значения должны быть больше. В случае если дерево сбалансировано, его высота К
будет
равна O(logN).
Важно заметить, что если при построении дерева ключи ее узлов идут в случайном порядке, то такие деревья называют сбалансированными. Такие рандомизированные деревья поиска обеспечивают сбалансированность только в вероятностном смысле. Они обладают следующими свойствами:
На рисунке ниже слева приведен пример сбалансированного дерева, которое получается для
ключей [ 9, 2, 13, 7, 1, 11, 14]
. Справа изображено несбалансированное дерево для упорядоченных
значений [ 1, 2, 7, 9, 11, 13, 14]
, вырожденное в односвязный список.
Существует несколько методов балансировки деревьев. Рассмотрим их чуть позже, а сейчас перейдем к основным операциям, связанных с деревьями.
В общем случае, двоичное дерево является графом, поэтому к нему применимы операции обхода в ширину и глубину о котором можно почитать по ссылкам ниже.
Однако для поиска значений узлов наиболее эффективнее было бы использовать свойство двоичных деревьев, что для любого ключа его левое поддерево содержит только меньшие ключи, а правое — только большие. Ниже представлена функция для поиска ключа в бинарном дереве.
from typing import Optional
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def __repr__(self):
return str(self.val)
def search_bst(node: Optional[TreeNode], val: int) -> Optional[TreeNode]:
"""
Поиск ключа в бинарном дереве
:param node: узле
:param val: искомое значение
:return: найденный узел, None если не найдено
"""
if not node:
return None
if node.val == val:
return node
if node.val > val:
return search_bst(node.left, val)
if node.val < val:
return search_bst(node.right, val)
Добавление элемента происходит по следующим правилам:
A
.A
в текущем узле, то новая вершина становится левым потомком, иначе –
правым.Удаление элемента является наиболее сложной операцией с точки зрения реализации. Возможны 3 варианта действий:
Узел является листом, т.е. не имеет потомков. Тогда мы можем просто его удалить.
Вершина имеет только одного ребенка. В этом случае мы удаляем саму вершину, а ее дочернее поддерево подвешиваем к родителю.
Если у узла два дочерних узла, то нужно найти следующий за ним элемент, т.е. минимум в правом поддереве (у этого элемента не будет левого потомка). Правого потомка подвесить на место найденного элемента, а удаляемый узел заменить найденным узлом.
Нетрудно заметить, что на рисунке выше у дерева слева максимальным элементом является значение 14. Это самый правый элемент, который не имеет правого ребенка (при этом левое поддерево может существовать). Аналогично, минимальным элементом дерева является значение 1. Это самый левый элемент, который не имеет левого ребенка.
Время работы всех перечисленных операций зависит от высоты дерева К
, т.е. равно O(logN) в случае
сбалансированности.
Итак, сложность всех операций с деревом зависит от его высоты. Следовательно, для эффективной работы нам нужно стараться минимизировать его высоту. Эта операция называется балансировкой. Кратко рассмотрим некоторые виды оптимизации деревьев.
АВЛ-дерево — сбалансированное двоичное дерево поиска, в котором поддерживается следующее свойство: для каждой его вершины высота её двух поддеревьев различается не более чем на 1. В процессе добавления и удаления узлов высота дочерних поддеревьев некоторых узлов может стать больше 2 или -2. Тогда следует провести ребалансировку дерева путем левого или правого поворота.
Красно-чёрным называется бинарное дерево, у которого каждому узлу сопоставлен дополнительный аттрибут – цвет, а также выполняются следующие свойства:
Подробнее здесь