Получить доступ

Что такое двоичное дерево поиска и зачем его балансировать?

JuniorСтруктуры данных
Ещё спрашивают так:
  • Какая сложность поиска в бинарном дереве?
  • Что такое AVL-дерево и красно-чёрное дерево?
  • Как обойти дерево (in-order, pre-order, post-order)?
Коротко

В двоичном дереве поиска для каждого узла слева — меньшие ключи, справа — большие; поиск идёт по одной ветке — O(высоты).

  • Сбалансированное дерево: высота O(log n); выродившееся (вставка по возрастанию) — O(n), как список
  • Балансировку обеспечивают AVL и красно-чёрные деревья поворотами

А ещё Обход in-order (лево–узел–право) выдаёт ключи отсортированными

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Попробуйте сами

Дерево поиска: вставка, поиск, повороты AVL

Подробно

Свойство дерева поиска: все ключи левого поддерева меньше ключа узла, все ключи правого — больше. Поиск, вставка и удаление начинаются с корня и на каждом шаге идут влево или вправо, поэтому стоят 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)» — только в сбалансированном.
  • Путать дерево поиска с двоичной кучей.

Что спросят дальше

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем