Menu
Coddy logo textTech

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

Последнее обновление

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

На сбалансированном дереве эти операции выполняются за время O(log n). Подвох: вставка уже отсортированных данных превращает дерево в связный список с операциями O(n) - именно поэтому существуют самобалансирующиеся варианты, такие как AVL- и красно-чёрные деревья.

Временная и пространственная сложность

ОперацияСбалансированноеХудший случай (вырожденное)
ПоискO(log n)O(n)
ВставкаO(log n)O(n)
УдалениеO(log n)O(n)
ПамятьO(n)O(n)

Пошагово (вставка)

ШагЧто происходит
1Если дерево пустое, новое значение становится корнем.
2Иначе начните с корня.
3Если значение меньше, идите к левому потомку; если больше - вправо.
4Повторяйте, пока не достигнете пустого места.
5Прикрепите там новое значение как лист.

Разобранный пример

Вставка [5, 3, 8, 1, 4] в пустое дерево, по одному значению за раз:

ВставкаПройденный путьДействие
5-Дерево пустое, поэтому 5 становится корнем.
353 < 5, идём влево; место пустое, прикрепляем 3 как левого потомка 5.
858 > 5, идём вправо; место пустое, прикрепляем 8 как правого потомка 5.
15 -> 31 < 5 идём влево, затем 1 < 3 идём влево; прикрепляем 1 как левого потомка 3.
45 -> 34 < 5 идём влево, затем 4 > 3 идём вправо; прикрепляем 4 как правого потомка 3.

Когда использовать двоичное дерево поиска

Используйте, когдаИзбегайте, когда
Нужен отсортированный порядок и быстрый поиск, а вставки поступают в случайном порядке.Ваши данные поступают уже отсортированными: несбалансированный BST деградирует до O(n) на операцию.
Вы хотите, чтобы симметричный обход бесплатно выдавал значения в отсортированной последовательности.Нужны только проверки принадлежности без порядка: хеш-таблица даёт в среднем O(1) при поиске.
Нужны запросы по диапазону или преемник/предшественник ключа.Нужны гарантированные границы O(log n): используйте самобалансирующееся AVL- или красно-чёрное дерево.
Набор данных часто меняется, а обновление статического отсортированного массива было бы дорогим.Набор данных фиксирован и только для чтения: отсортированный массив с двоичным поиском проще и дружелюбнее к кешу.

Код Binary Search Tree

Чистая, готовая к запуску реализация Binary Search Tree на Python, JavaScript, Java, C++, C. Выберите язык, скопируйте код или откройте его уже загруженным в плейграунде Coddy.

Код Binary Search Tree на Python

Python
1class Node:2    def __init__(self, key):3        self.key = key4        self.left = None5        self.right = None6
7
8def insert(node, key):9    if node is None:10        return Node(key)11    if key < node.key:12        node.left = insert(node.left, key)13    elif key > node.key:14        node.right = insert(node.right, key)15    return node  # duplicates are ignored16
17
18def search(node, key):19    if node is None:20        return False21    if key == node.key:22        return True23    if key < node.key:24        return search(node.left, key)25    return search(node.right, key)26
27
28def inorder(node):29    if node is None:30        return []31    return inorder(node.left) + [node.key] + inorder(node.right)32
33
34root = None35for key in [8, 3, 10, 1, 6, 14, 4, 7]:36    root = insert(root, key)37
38print("Inorder (sorted):", inorder(root))39print("search(6): ", search(root, 6))40print("search(5): ", search(root, 5))
Запустите этот код в плейграунде Python

Вопросы и ответы о двоичном дереве поиска

Какова временная сложность двоичного дерева поиска?
Поиск, вставка и удаление — O(log n) на сбалансированном дереве, потому что каждое сравнение отбрасывает половину оставшихся узлов. В худшем случае — дерево, вырожденное в цепочку из-за отсортированных вставок — они деградируют до O(n).
В чём разница между двоичным деревом и двоичным деревом поиска?
Двоичное дерево — это любое дерево, где у каждого узла не более двух потомков, без правила упорядочивания. Двоичное дерево поиска добавляет инвариант: значения левого поддерева меньше, а правого больше значения узла, что и обеспечивает быстрый поиск.
Почему двоичное дерево поиска может стать медленным?
Если значения вставляются в отсортированном (или обратно отсортированном) порядке, каждый новый узел идёт в одну и ту же сторону, образуя высокое вырожденное дерево, ведущее себя как связный список — O(n) на операцию. Самобалансирующиеся деревья (AVL, красно-чёрные) вращают узлы, чтобы предотвратить это.
В чём разница между двоичным деревом поиска и хеш-таблицей?
Хеш-таблица даёт в среднем O(1) при поиске, но хранит ключи без определённого порядка, поэтому не может отвечать на запросы по диапазону или преемнику. Двоичное дерево поиска немного медленнее с O(log n), но хранит ключи упорядоченными, позволяя обходить их по порядку и находить ближайшие значения. Выбирайте BST, когда важен порядок, и хеш-таблицу, когда нужны только проверки принадлежности.
Когда следует использовать самобалансирующееся дерево вместо простого BST?
Используйте самобалансирующееся дерево (AVL, красно-чёрное) всякий раз, когда не можете контролировать порядок вставки и нуждаетесь в гарантированной производительности O(log n). Простой BST подходит для обучения, для небольших наборов данных или когда ключи поступают в случайном порядке, но он не защищает от вырожденного худшего случая.
Возвращает ли симметричный обход BST отсортированные значения?
Да. Посещение левого поддерева, затем узла, затем правого поддерева выдаёт ключи по возрастанию, что напрямую следует из инварианта BST. Распространённая ошибка — ожидать того же от простого двоичного дерева: без правила упорядочивания симметричный обход не даёт никакой осмысленной последовательности.
Coddy programming languages illustration

Освойте алгоритмы с Coddy

НАЧАТЬ