1. Лекция 6:
Биномиальные кучи
(Binomial heaps)
Курносов Михаил Георгиевич
к.т.н. доцент Кафедры вычислительных систем
Сибирский государственный университет
телекоммуникаций и информатики
http://www.mkurnosov.net
2. Значение (Value) Приоритет (Priority)
Слон 3
Кит 1
Лев 15
Очередь с приоритетом (Priority queue)
2
Очередь с приоритетом (Priority queue) –
это очередь, в которой элементы имеют приоритет (вес)
Поддерживаемые операции:
o Insert(key, value) – добавляет в очередь значение value
c приоритетом (весом, ключом) key
o DeleteMin/DeleteMax – удаляет из очереди элемент
с мин./макс. приоритетом (ключом)
o Min/Max – возвращает элемент с мин./макс. ключом
o DecreaseKey – изменяет значение ключа заданного элемента
o Merge(q1, q2) – сливает две очереди в одну
3. Двоичная куча (Binary heap)
3
Двоичная куча (пирамида, сортирующее дерево,
binary heap) – это двоичное дерево, удовлетворяющее
следующим условиям:
a) приоритет (ключ) любой вершины не меньше ( ≥ ),
приоритета её потомков
b) дерево является полным двоичным деревом
(complete binary tree) –
все уровни заполнены, возможно
за исключением последнего
4. Двоичная куча (Binary heap)
4
max-heap
Приоритет любой вершины
не меньше (≥),
приоритета потомков
min-heap
Приоритет любой вершины
не больше (≤),
приоритета потомков
5. Очередь с приоритетом (Priority queue)
5
В таблице приведены трудоемкости операций очереди
с приоритетом (в худшем случае, worst case)
Символом ‘*’ отмечена амортизированная сложность операций
Операция
Binary
heap
Binomial
heap
Fibonacci
heap
Pairing
heap
Brodal
heap
FindMin Θ(1) O(logn) Θ(1) Θ(1)* Θ(1)
DeleteMin Θ(logn) Θ(logn) O(logn)* O(logn)* O(logn)
Insert Θ(logn) O(logn) Θ(1) Θ(1)* Θ(1)
DecreaseKey Θ(logn) Θ(logn) Θ(1)* O(logn)* Θ(1)
Merge/Union Θ(n) Ω(logn) Θ(1) Θ(1)* Θ(1)
6. Биномиальные кучи (Binomial heaps)
6
Биномиальная куча (Binomial heap, пирамида) –
это эффективно сливаемая куча (mergeable heap)
В биномиальной куче слияние (merge, union) двух куч
выполняется за время O(logn)
Биномиальные кучи формируются на основе
биномиальных деревьев (binomial trees)
7. Биномиальное дерево (Binomial tree)
7
Биномиальное дерево Bk (Binomial tree) –
это рекурсивно определяемое дерево высоты k, в котором:
o количество узлов равно 2k
o количество узлов на уровне i = 0, 1, …, k:
𝑘
𝑖
=
𝑘!
𝑖! 𝑘−𝑖 !
(количество сочетаний из k по i)
o корень имеет k дочерних узлов:
− первый дочерний узел (самый левый) – дерево Bk-1
− второй дочерний узел (второй слева) – дерево Bk-2
− …
− k-й дочерний узел (самый правый) – дерево B0
9. Биномиальная куча (Binomial heap)
9
Биномиальная куча (Binomial heap) –
это множество биномиальных деревьев, которые
удовлетворяют свойствам биномиальных куч:
1) каждое биномиальное дерево упорядочено (ordered)
в соответствии со свойствами неубывающей
или невозрастающей кучи (min-heap/max-heap):
ключ узла не меньше/не больше ключа его родителя
2) для любого целого k ≥ 0 имеется не более одного
биномиального дерева, чей корень имеет степень k
Биномиальная куча содержащая n узлов состоит
не более чем из log(𝑛) + 1 биномиальных деревьев
11. Биномиальное дерево (Binomial tree)
11
Пусть биномиальная куча содержит n узлов
Если записать n в двоичной системе исчисления,
то номера ненулевых битов будут соответствовать
степеням биномиальных деревьев, образующих кучу
6
2914
38
8
1711
27
1
2512
18
10
Биномиальная куча из 13 узлов (деревья B0, B2 и B3)
1310 = 11012
12. Биномиальное дерево (Binomial tree)
12
Корни биномиальных деревьев биномиальной кучи
хранятся в односвязном списке – списке корней (root list)
В списке корней узлы упорядочены по возрастанию их
степеней (степеней корней биномиальных деревьев кучи)
6
2914
38
8
1711
27
1
2512
18
10
Биномиальная куча из 13 узлов (деревья B0, B2 и B3)
1310 = 11012
Head
13. Узел биномиальной кучи
13
Каждый узел биномиальной кучи
(биномиального дерева) содержит следующие поля:
parent
key
value
degree
child sibling
o key – приоритет узла (вес, ключ)
o value – данные
o degree – количество дочерних узлов
o parent – указатель на родительский узел
o child – указатель на крайний левый
дочерний узел
o sibling – указатель на правый
сестринский узел
15. Поиск минимального узла (FindMin)
15
В корне каждого биномиального дерева хранится
его минимальный/максимальный ключ
Для поиска минимального/максимального элемента
в биномиальной куче требуется пройти по списку
из log(𝑛) + 1 корней
6
2914
38
8
1711
27
1
2512
18
10Head
16. Поиск минимального узла (FindMin)
16
function BinomialHeapMin(heap)
x = heap
min.key = Infinity
while x != NULL do
if x.key < min.key then
min = x
x = x.sibling
end while
return min
end function
6
2914
38
8
1711
27
1
2512
18
10Head
TMin = O(logn)
17. function BinomialHeapMin(heap)
x = heap
min.key = Infinity
while x != NULL do
if x.key < min.key then
min = x
x = x.sibling
end while
return min
end function
Поиск минимального узла (FindMin)
17
6
2914
38
8
1711
27
1
2512
18
10Head
Как реализовать поиск
минимального/максимального ключа за время O(1)?
Поддерживать указатель на корень дерева (узел),
в котором находится экстремальный ключ
TMin = O(logn)
18. Слияние куч (Union)
18
Для слияния (union, merge) двух куч H1 и H2
в новую кучу H необходимо:
1. Слить списки корней H1 и H2 в один
упорядоченный список
2. Восстановить свойства биномиальной кучи H
После слияния списков корней известно, что в куче H
имеется не более двух корней с одинаковой степенью
и они соседствуют
21. Слияние списков корней
function BinomialHeapListMerge(h1, h2)
h = NULL
while h1 != NULL && h2 != NULL do
if h1.degree <= h2.degree then
LinkedList_AddEnd(h, h1)
h1 = h1.next
else
LinkedList_AddEnd(h, h2)
h2 = h2.next
end if
end while
while h1 != NULL do
LinkedList_AddEnd(h, h1)
h1 = h1.next
end while
while h2 != NULL do
LinkedList_AddEnd(h, h2)
h2 = h2.next
end while
return h
end function
TMerge = O(log(max(n1, n2)))
21
23. 23
Случай 3
x.degree = next-x.degree ≠ next-x.sibling.degree
x.key ≤ next-x.key
a b c
prev-x x next-x
d a b
c
prev-x x next-x
d
Bk Bk Bl
Bk
Bk Bl
Bk+1
x.sibling = next-x.sibling
BinomialTreeLink(next-x, x)
next-x = x.sibling
27. Случай 2
27
a b c
prev-x x next-x
d a b c
prev-x x next-x
d
Bk Bk Bk Bk Bk Bk
x.degree = next-x.degree = next-x.sibling.degree
/* Перемещаем указатели по списку корней */
prev-x = x
x = next-x
next-x = x.sibling
28. Случай 2
28
x.degree = next-x.degree = next-x.sibling.degree
/* Перемещаем указатели по списку корней */
prev-x = x
x = next-x
next-x = x.sibling
6
4410
17
29
3148
50
3
3718 8
2223
24
30
3245
55
15
3328
41
7
25
12
H
x next-xprev-x
29. 29
prev-x.sibling = next-x
BinomialTreeLink(x, next-x)
x = next-x
next-x = x.sibling
Случай 4
x.degree = next-x.degree ≠ next-x.sibling.degree
x.key > next-x.key
a b c
prev-x x next-x
d a c
b
prev-x x next-x
d
Bk Bk Bl
Bk
Bk Bl
Bk+1
30. Случай 1
30
x.degree ≠ next-x.degree
Узел x – корень дерева Bk
Узел next-x – корень дерева Bl, l > k
/* Перемещаем указатели по списку корней */
prev-x = x
x = next-x
next-x = x.sibling
a b c
prev-x x next-x
d a b c
prev-x x next-x
d
Bk Bl Bk Bl
l > k
31. Слияние биномиальных куч (Union)
function BinomialHeapUnion(h1, h2)
h = BinomialHeapListMerge(h1, h2)
prev-x = NULL
x = h
next-x = x.sibling
while next-x != NULL do
if (x.degree != next-x.degree) OR
(next-x.sibling != NULL AND
next-x.sibling.degree = x.degree)
then
prev-x = x /* Случаи 1 и 2 */
x = next-x
else if x.key <= next-x.key then
x.sibling = next-x.sibling /* Случай 3 */
BinomialTreeLink(next-x, x)
31
32. Слияние биномиальных куч (Union)
else
/* Случай 4 */
if prev-x = NULL then
h = next-x
else
prev-x.sibling = next-x
end if
BinomialTreeLink(x, next-x)
x = next-x
end if
next-x = x.sibling
end while
return h
end function
32
TUnion = O(logn)
33. Слияние биномиальных куч (Union)
33
Вычислительная сложность слияния двух
биномиальных куч в худшем случае равна O(log(n))
Длина списка корней не превышает
log(n1) + log(n1) + 2 ≤ log(n) + 2 = O(log(n))
Цикл while в функции BinomialHeapUnion выполняется
не более O(log(n)) раз
На каждой итерации цикла указатель перемещается
по списку корней вправо на одну позицию или удаляется
один узел – это требует времени O(1)
34. Вставка узла (Insert)
34
Создаем биномиальную кучу из одного узла Zx –
биномиального дерева B0
Сливаем исходную кучу H и кучу из узла x
function BinomialHeapInsert(h, key, value)
x.key = key
x.value = value
x.degree = 0
x.parent = NULL
x.child = NULL
x.sibling = NULL
return BinomialHeapUnion(h, x)
end function
TInsert = O(logn)
35. Удаление минимального узла (DeleteMin)
35
1. В списке корней кучи H отыскиваем корень x
с минимальным ключом и удаляем x из списка корней
(разрывам связь)
2. Инициализируем пустую кучу Z
3. Меняем порядок следования дочерних узлов корня х
на обратный, у каждого дочернего узла устанавливаем
поле parent в NULL
4. Устанавливаем заголовок кучи Z на первый элемент
нового списка корней
5. Сливаем кучи H и Z
6. Возвращаем x
36. Удаление минимального узла (DeleteMin)
36
1
2512
18
16
3326
42
37
41 6
2914
38
8
1711
27
10
1328
77
H
В списке корней кучи H отыскиваем корень x
с минимальным ключом и удаляем x из списка корней
(разрывам связь)
x
37. Удаление минимального узла (DeleteMin)
37
1
2512
18
16
3326
42
37
41 6
2914
38
8
1711
27
10
1328
77
H
Меняем порядок следования дочерних узлов корня х
на обратный, у каждого дочернего узла устанавливаем
поле parent в NULL
Дочерние узлы корня x образуют биномиальную кучу Z
x
38. Удаление минимального узла (DeleteMin)
38
25 12
18
16
3326
42
37
41
6
2914
38
8
1711
27
10
1328
77
H
Сливаем кучи H и Z
Z
39. Удаление минимального узла (DeleteMin)
function BinomialHeapDeleteMin(h)
/* Lookup and unlink min node */
x = h
xmin.key = Infinitya
prev = NULL
while x != NULL do
if x.key < xmin.key then
xmin = x
prevmin = prev
end if
prev = x
x = x.sibling
end while
if prevmin != NULL then
prevmin.sibling = xmin.sibling
else
h = xmin.sibling
39
40. Удаление минимального узла (DeleteMin)
/* Reverse linked list */
child = xmin.child
prev = NULL
while child != NULL do
sibling = child.sibling
child.sibling = prev
prev = child
child = sibling
end while
return BinomialHeapUnion(h, prev)
end function
40
TDeleteMin = O(logn)
41. Уменьшение ключа (DecreaseKey)
41
1. Получаем указатель на узел x и изменяем у него ключ
(newkey <= x.key)
2. Проверяем значение ключа родительского узла,
если он меньше ключа x, то выполняем обмен ключей (и
данных), повторяем обмены пока не поднимемся до корня
текущего биномиального дерева
6
2914
38
8
175
27
1
2512
18
10Head
x
p
42. Уменьшение ключа (DecreaseKey)
42
function BinomialHeapDecreaseKey(h, x, key)
if x.key < k then
return Error
x.key = key
y = x
z = y.parent
while z != NULL AND y.key < z.key do
temp = y.key
y.key = z.key
z.key = temp
y = z
z = y.parent
end while
end function
TDecreaseKey = O(logn)
43. Удаление узла (Delete)
43
function BinomialHeapDelete(h, x)
BinomialHeapDecreaseKey(h, x, -Infinity)
BinomialHeapDeleteMin(h)
end function
TDelete = O(logn)
44. Узел биномиального дерева
struct bmheap {
int key;
char *value;
int degree;
struct bmheap *parent;
struct bmheap *child;
struct bmheap *sibling;
};
44
45. Пример использования bmheap
int main()
{
struct bmheap *h = NULL, *node;
h = bmheap_insert(h, 10, “10”);
h = bmheap_insert(h, 12, “12”);
h = bmheap_insert(h, 3, “3”);
h = bmheap_insert(h, 56, “56”);
node = bmheap_min(h);
printf("Min = %dn", node->key);
h = bmheap_deletemin(h);
bmheap_free(h);
return 0;
}
45