Биномиальные очереди, которые мы, чтобы избежать путаницы с очередями FIFO, будем называть биномиальными кучами (binomial heaps) еще одна распространенная реализация куч. Биномиальные кучи устроены сложнее, чем левоориентированные, и, на первый взгляд, не возмещают эту сложность никакими преимуществами. Однако в последующих главах мы увидим, как в различных вариантах биномиальных куч можно заставить insert и merge выполняться за время O(1).
Биномиальные кучи строятся из более простых объектов, называемых биномиальными деревьями. Биномиальные деревья индуктивно определяются так:
* Биномиальное дерево ранга 0 представляет собой одиночный узел.
* Биномиальное дерево ранга r+1 получается путем связывания (linking) двух биномиальных деревьев ранга r, так что одно из них становится самым левым потомком второго.
Из этого определения видно, что биномиальное дерево ранга r содержит ровно 2r элементов. Существует второе, эквивалентное первому, определение биномиальных деревьев, которым иногда удобнее пользоваться: биномиальное дерево ранга r представляет собой узел с r потомками t1 . . . tr , где каждое ti является биномиальным деревом ранга r-i. На Рис. показаны биномиальные деревья рангов от 0 до 3.
Мы представляем вершину биномиального дерева в виде элемента и списка его потомков. Для удобства мы также помечаем каждый узел его рангом.
![Структура данных буферного кэша](https://whoisdeveloper.ru/static/img/sd1.png)
```
datatype Tree = Node of int x Elem.T x Tree list
```
Каждый список потомков хранится в убывающем порядке рангов, а элемен- ты хранятся с порядком кучи. Чтобы сохранять этот порядок, мы всегда привязываем дерево с большим корнем к дереву с меньшим корнем.
```
fun link (t1 as Node (r, x1, c1), t2 as Node (_, x2, c2)) =
if Elem.leq(x1,x2) then Node(r+1,x1,t2 ::c1)
else Node(r+1,x2,t1 ::c2)
```
Связываем мы всегда деревья одного ранга.
Теперь определяем биномиальную кучу как коллекцию биномиальных
деревьев, каждое из которых имеет порядок кучи, и никакие два дерева не совпадают по рангу. Мы представляем эту коллекцию в виде списка деревьев в возрастающем порядке ранга.
```
Type Heap = Tree list
```
Поскольку каждое биномиальное дерево содержит 2^r элементов, и никакие два дерева по рангу не совпадают, деревья размера n в точности соответствуют единицам в двоичном представлении n. Например, число 21 в двоичном виде выглядит как 10101, и поэтому биномиальная куча размера 21 содержит одно дерево ранга 0, одно ранга 2, и одно ранга 4 (размерами, соответственно, 1, 4 и 16). Заметим, что так же, как двоичное представление n содержит не более log(n + 1) единиц, биномиальная куча размера n содержит не более log(n + 1) деревьев.
Теперь мы готовы описать функции, действующие на биномиальных деревьях. Начинаем мы с insert и merge, которые определяются примерно ана- логично сложению двоичных чисел. Чтобы внести элемент в кучу, мы сначала создаем одноэлементное дерево (т. е., биномиальное дерево ранга 0), затем поднимаемся по списку суще- ствующих деревьев в порядке возрастания рангов, связывая при этом одноранговые деревья. Каждое связывание соответствует переносу в двоичной арифметике.
```
fun rank (Node (r, x, c)) = r
fun insTree (t,[]) = [t]
| insTree (t, ts as t' :: ts') =
if rank t < rank t' then t :: ts else insTree (link (t, t'), ts')
```
```
fun insert (x, ts) = insTree (Node (0, x, []) , ts)
```
В худшем случае, при вставке в кучу размера n = 2k^1, требуется k связываний и O(k) = O(log n) времени.
При слиянии двух куч мы проходим через оба списка деревьев в порядке возрастания ранга и связываем по пути деревья равного ранга. Как и преж- де, каждое связывание соответствует переносу в двоичной арифметике.
```
fun merge (ts1 , []) =ts1
| merge ([], ts2)=ts2
| merge(ts1 as t1 ::ts'1, ts2 as t2 ::ts'2)=
if rank t1 < rank t2 then t1 :: merge (ts'1, ts2)
else if rank t2 < rank t1 then merge (ts1, ts'2)
else insTree (link (t1 , t2), merge (ts'1 , ts'2))
```
Функции findMin и deleteMin вызывают вспомогательную функцию removeMinTree, которая находит дерево с минимальным корнем, исключает его из списка
и возвращает как это дерево, так и список оставшихся деревьев.
```
fun removeMinTree [t] = (t, [])
| removeMinTree (t :: ts) =
let val (t ' , ts ') = removeMinTree ts
in if Elem.leq (root t, root t') then (t, ts) else (t', t :: ts') end
```
Функция findMin просто возвращает корень найденного дерева
```
fun findMin ts = let val (t , _) = removeMinTree ts in root t end
```
Функция deleteMin устроена немного похитрее. Отбросив корень найденного дерева, мы еще должны вернуть его потомков в список остальных деревьев. Заметим, что список потомков почти уже соответствует определению би номиальной кучи. Это коллекция биномиальных деревьев с неповторяющимися рангами, но только отсортирована она не по возрастанию, а по убыванию ранга. Таким образом, обратив список потомков, мы преобразуем его в биномиальную кучу, а затем сливаем с оставшимися деревьями.
```
fun deleteMin ts = let val (Node (_, x, ts1), ts2) = removeMinTree ts
in merge (rev ts1, ts2) end
```
Полная реализация биномиальных куч
```
functor BinomialHeap(Element: ORDERED) : Heap = struct
structure Elem = Element
datatype Tree = Node of int $\times$ Elem.T $\times$ Tree list
datatype Heap = Tree list
val empty = []
val isEmpty ts = null ts
fun rank (Node(r,x,c)) = r
fun root (Node(r,x,c)) = x
fun link ($t_1$ as Node ($r_1$,$x_1$,$c_1$), $t_2$ as Node ($r_2$,$x_2$,$c_2$)) =
if Elem.leq ($x_1$,$x_2$) then Node(r+1, $x_1$, $t_2$::$c_1$)
else Node(r+1, $x_2$, $t_1$::$c_2$)
fun insTree (t,[]) = [t]
| insTree (t, ts as t'::ts') =
if rank t < rank t' then t::ts else insTree(link(t,t'), ts')
fun insert (x,ts) = insTree(Node(0,x,[]), ts)
fun merge ($ts_1$, []) $=$ $ts_1$
| merge ([], $ts_2$) $=$ $ts_2$
| merge ($ts_1$ as $t_1$::$ts_1'$, $ts_2$ as $t_2$::$ts_2'$) =
if rank $t_1$ < rank $t_2$ then $t_1$ :: merge($ts_1$,$ts_2'$)
else if rank $t_2$ < rank $t_1$ then $t_2$ :: merge($ts_2$, $ts_1'$)
else insTree(link($t_1$,$t_2$), merge($ts_1'$,$ts_2'$)
fun removeMinTree [] = raise EMPTY
| removeMinTree [t] = (t,[])
| removeMinTree (t::ts) =
let val ($t'$, $ts'$) $=$ removeMinTree ts
in if Elem.leq (root t, root t') then (t,ts) else (t', t::ts') end
fun findMin ts = let val (t,_) = removeMinTree ts in root t end
fun deleteMin ts =
let val (Node(_, x,$ts_1$), $ts_2$) $=$ removeMinTree ts
in merge (rev $ts_1$,$ts_2$) end
end
```