Биномиальная куча: операции и слияние за O(log n)

Биномиальная куча - структура данных типа «приоритетная очередь», которая поддерживает все стандартные операции кучи (insert, find-min, extract-min, decrease-key, delete) за и при этом умеет дополнительно объединять две кучи (merge) за то же время. Бинарная куча на массиве этого не умеет: её слияние требует . Биномиальная куча относится к семейству mergeable heap и служит мостом между обычной бинарной кучей и более продвинутой Фибоначчиевой.
Биномиальное дерево
Биномиальное дерево определяется рекурсивно:
- - одиночный узел.
- при получается из двух копий : одна копия становится самым левым ребёнком корня другой.
Иначе говоря, чтобы построить , берут два и подвешивают второй к корню первого. Корень растущего дерева получает ещё одного сына, его степень увеличивается на единицу.
Из определения сразу следуют ключевые числовые свойства:
- В ровно узлов.
- Высота равна .
- На глубине (от до ) находится узлов - отсюда и имя «биномиальное».
- Степень корня равна , и его дети - корни деревьев в порядке убывания ранга.
Эти свойства легко доказываются индукцией: при склейке двух удваивается число узлов (), а число элементов на уровне нового дерева складывается из элементов уровня старого дерева и уровня второго - это тождество Паскаля .
Биномиальная куча как лес
Биномиальная куча размера - это лес биномиальных деревьев, удовлетворяющий двум условиям:
- Heap-order property. В каждом дереве ключ родителя не больше ключа любого ребёнка (для min-кучи). Минимум всей кучи лежит в корне одного из деревьев.
- Уникальность рангов. В лесу не может быть двух деревьев с одинаковым . Если в двоичной записи, то присутствует в куче тогда и только тогда, когда .
Это даёт прямое соответствие «куча размера » «двоичная запись ». Число деревьев в лесу не превосходит - именно поэтому все операции укладываются в логарифм.
Корни деревьев соединяют в односвязный список, упорядоченный по возрастанию ранга. У каждого узла хранят ключ, степень, указатель на родителя, на самого левого ребёнка и на правого «брата» (sibling).
Объединение двух куч как сложение двоичных чисел
Операция merge - сердце биномиальной кучи. Пусть есть две кучи размера и размера . Их корневые списки уже отсортированы по рангу - сначала их сливают в один список (как в merge-sort, за ).
Затем по списку идут слева направо и применяют правило: если встречаются два соседних дерева одинакового ранга , их сливают в одно дерево - корень с большим ключом становится ребёнком корня с меньшим. Это даёт «перенос» в следующий разряд - ровно как при сложении в двоичной системе.
Аккуратность нужна, когда подряд идут три дерева ранга : тогда первое оставляют как есть, а второе и третье склеивают (это соответствует случаю при сложении в столбик с уже имеющимся переносом). В итоге результирующий лес снова состоит из деревьев с уникальными рангами, а суммарное время - .
Основные операции
Через merge выражаются почти все остальные операции.
find-min- пройти по корневому списку, выбрать минимум среди корней. Время: . Если хранить указатель на минимальный корень и обновлять его при каждой модификации, получается amortized.insert(x)- создать однонодовую кучу-сингл и слить её с основной. Амортизированно , в худшем случае .extract-min- найти корень с минимальным ключом, удалить его из корневого списка, перевернуть список его детей (они идут , а для merge нужен возрастающий порядок) и слить полученный лес с оставшейся частью. Время: .decrease-key(v, k')- заменить ключ узла на меньший и «всплыть» вверх, обменивая местами с родителем, пока heap-order не восстановится. Высота не превышает , отсюда . Реальные данные при swap переносить накладно - обычно меняют только значения ключей и сателлитные данные.delete(v)- выполняется какdecrease-key(v, -\infty)с последующимextract-min. Тоже .buildиз массива - последовательныеinsertдают амортизированно (геометрическая сумма переносов аналогична построению бинарной кучи).
Сводная таблица сложностей:
Ленивая версия и Фибоначчиева куча
Если откладывать «приведение в порядок» (объединение деревьев одинаковых рангов) до момента extract-min, получится ленивая биномиальная куча: insert и merge становятся в худшем случае, а вся работа концентрируется в extract-min, который теперь amortized . На этой идее построена Фибоначчиева куча (Fredman, Tarjan, 1984): она добавляет каскадные срезы при decrease-key и достигает амортизированного для всех операций, кроме extract-min и delete ( amortized).
Фибоначчиева куча асимптотически лучше биномиальной для алгоритма Дейкстры на плотных графах, но имеет большие константы и сложнее в реализации - поэтому в учебных и инженерных задачах биномиальная куча остаётся компромиссом «простота против гибкости».
Где применяется
- Алгоритмы кратчайших путей и MST. Дейкстра и Прим на разреженных графах - там, где
decrease-keyвызывается часто. Биномиальная куча даёт . - Mergeable priority queue. Когда задачи разбиваются по подсистемам и затем нужно объединить их очереди - например, в распределённых системах или при слиянии планировщиков двух процессов.
- Дискретно-событийное моделирование. Когда несколько генераторов событий ведут отдельные очереди, которые объединяются в общую таймлайн.
- Branch-and-bound / A* с подзадачами. Подзадачи каждого поддерева можно держать в собственной куче и сливать при обходе.
Сравнение: бинарная, биномиальная, Фибоначчиева
| Операция | Бинарная (массив) | Биномиальная | Фибоначчиева |
|---|---|---|---|
find-min | / | ||
insert | amort. | ||
extract-min | amort. | ||
decrease-key | amort. | ||
merge |
Бинарная куча выигрывает по константам и локальности памяти, но проигрывает там, где нужны частые слияния. Биномиальная - сбалансированный «mergeable» вариант. Фибоначчиева оптимальна асимптотически, но дорога в реализации и константах.
Частые ошибки
- Путают с полным бинарным деревом высоты . В ровно узлов, а в полном бинарном дереве высоты их . Структуры разные: у корень имеет степень .
- Считают, что в куче может быть несколько деревьев одного ранга. Нет - это нарушает инвариант, ровно как «две единицы в одном разряде» нарушают двоичную запись. Любая операция, нарушившая правило, обязана выполнить серию сливаний.
- Забывают развернуть список детей при
extract-min. Дети корня хранятся в порядке убывания ранга, а корневой список - в порядке возрастания. Без разворота merge даст неверный лес. - Пытаются делать
decrease-keyчерез указатель в массив. В биномиальной куче нет фиксированного массива: чтобы быстро найти узел по «дескриптору», операцияinsertдолжна возвращать handle на конкретный узел, а пользователь обязан хранить его. - Меряют производительность только по асимптотике. На малых и без
mergeбинарная куча почти всегда быстрее: меньше указателей, лучше кэш.
FAQ
Чем биномиальная куча отличается от бинарной?
Бинарная - это массив с heap-order, она проста и кэш-дружелюбна, но merge требует . Биномиальная - лес деревьев, merge за ценой указательной структуры и больших констант.
Почему именно «биномиальная»? Потому что на глубине дерева ровно узлов - биномиальный коэффициент. Это прямо следует из рекурсивного определения и тождества Паскаля.
Когда выбирать биномиальную, а когда Фибоначчиеву?
Если в алгоритме доминирует decrease-key (плотные графы в Дейкстре, специальные алгоритмы MST) - Фибоначчиева теоретически быстрее. Если важна простота, отладка и предсказуемость - биномиальная или даже бинарная.
Коротко
Биномиальная куча - лес биномиальных деревьев с уникальными рангами и heap-order; число и форма деревьев определяются двоичной записью . Все базовые операции работают за , а ключевая merge сводится к сложению двоичных чисел с переносами. Это удобная mergeable-структура, более гибкая, чем бинарная куча, и более простая в реализации, чем Фибоначчиева.
Читайте также

Куча Фибоначчи: ленивая структура и амортизация
Куча Фибоначчи: амортизированный на insert и decrease-key, ленивая консолидация при extract-min, потенциал, каскадный cut и применение в алгоритме Дейкстры.

B-дерево: вставка ключа и разделение узла
Разбираем вставку в B-дерево по шагам: минимальная степень t, инвариант t-1..2t-1 ключей и разделение переполненного узла с подъёмом среднего ключа к родителю.

AVL-дерево: как работает балансировка и ротации
Разбираем, как AVL-дерево восстанавливает баланс после вставки и удаления: инвариант высоты, balance factor и четыре ротации LL, RR, LR, RL за O(log n).