Что такое двоичное дерево поиска и зачем его балансировать?
- Какая сложность поиска в бинарном дереве?
- Что такое AVL-дерево и красно-чёрное дерево?
- Как обойти дерево (in-order, pre-order, post-order)?
В двоичном дереве поиска для каждого узла слева — меньшие ключи, справа — большие; поиск идёт по одной ветке — O(высоты).
- Сбалансированное дерево: высота O(log n); выродившееся (вставка по возрастанию) — O(n), как список
- Балансировку обеспечивают AVL и красно-чёрные деревья поворотами
А ещё Обход in-order (лево–узел–право) выдаёт ключи отсортированными
Подробно
Свойство дерева поиска: все ключи левого поддерева меньше ключа узла, все ключи правого — больше. Поиск, вставка и удаление начинаются с корня и на каждом шаге идут влево или вправо, поэтому стоят O(h), где h — высота дерева.
Проблема вырождения. Если вставлять ключи по возрастанию, каждый новый ключ уходит вправо, и дерево превращается в цепочку высотой n — операции становятся O(n).
Сбалансированные деревья поддерживают высоту O(log n):
- AVL — высоты поддеревьев любого узла отличаются не больше чем на 1; после вставки нарушение исправляют поворотами. Строже сбалансировано — быстрее поиск;
- красно-чёрное — более мягкое правило через раскраску узлов, меньше поворотов при вставке. Используется в
TreeMapв Java иstd::mapв C++.
Обходы дерева:
- in-order (лево, узел, право) — ключи по возрастанию;
- pre-order (узел, лево, право) — копирование и сериализация дерева;
- post-order (лево, право, узел) — удаление, подсчёт размеров поддеревьев;
- по уровням — обход в ширину через очередь.
Сравнение с хеш-таблицей: хеш быстрее в среднем (O(1)), а дерево хранит ключи упорядоченными — даёт минимум, максимум, диапазоны и ближайший ключ.
Пример
class Node:
def __init__(self, key):
self.key, self.left, self.right = key, None, None
def insert(root, key):
if root is None:
return Node(key)
if key < root.key:
root.left = insert(root.left, key)
elif key > root.key:
root.right = insert(root.right, key)
return root
def inorder(root):
return inorder(root.left) + [root.key] + inorder(root.right) if root else []Что хотят услышать
- Свойство упорядоченности и сложность O(высоты).
- Проблема вырождения и смысл балансировки.
- Названия сбалансированных деревьев.
- Виды обхода и сравнение с хеш-таблицей — плюс.
Частые ошибки
- «Поиск в бинарном дереве всегда O(log n)» — только в сбалансированном.
- Путать дерево поиска с двоичной кучей.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗Слева большие ключи, справа меньшие; поиск всегда O(log n) независимо от порядка вставки, поэтому балансировка нужна только для экономии памяти
- ✓Слева меньшие ключи, справа большие; поиск O(высоты), поэтому дерево балансируют, иначе вставка по возрастанию даёт O(n)
- ✗Обход in-order выдаёт ключи в случайном порядке
- ✗Балансировка нужна только для экономии памяти
Что спросят дальше
Связанные вопросы
- Как работает бинарный поиск? Junior
- Как устроена хеш-таблица? Junior
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.