Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
Лекция 6:
Биномиальные кучи
(Binomial heaps)
Курносов Михаил Георгиевич
к.т.н. доцент Кафедры вычислительных систем
Сибирский государственный университет
телекоммуникаций и информатики
http://www.mkurnosov.net
Значение (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) – сливает две очереди в одну
Двоичная куча (Binary heap)
3
 Двоичная куча (пирамида, сортирующее дерево,
binary heap) – это двоичное дерево, удовлетворяющее
следующим условиям:
a) приоритет (ключ) любой вершины не меньше ( ≥ ),
приоритета её потомков
b) дерево является полным двоичным деревом
(complete binary tree) –
все уровни заполнены, возможно
за исключением последнего
Двоичная куча (Binary heap)
4
max-heap
Приоритет любой вершины
не меньше (≥),
приоритета потомков
min-heap
Приоритет любой вершины
не больше (≤),
приоритета потомков
Очередь с приоритетом (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)
Биномиальные кучи (Binomial heaps)
6
 Биномиальная куча (Binomial heap, пирамида) –
это эффективно сливаемая куча (mergeable heap)
 В биномиальной куче слияние (merge, union) двух куч
выполняется за время O(logn)
 Биномиальные кучи формируются на основе
биномиальных деревьев (binomial trees)
Биномиальное дерево (Binomial tree)
7
 Биномиальное дерево Bk (Binomial tree) –
это рекурсивно определяемое дерево высоты k, в котором:
o количество узлов равно 2k
o количество узлов на уровне i = 0, 1, …, k:
𝑘
𝑖
=
𝑘!
𝑖! 𝑘−𝑖 !
(количество сочетаний из k по i)
o корень имеет k дочерних узлов:
− первый дочерний узел (самый левый) – дерево Bk-1
− второй дочерний узел (второй слева) – дерево Bk-2
− …
− k-й дочерний узел (самый правый) – дерево B0
Биномиальное дерево (Binomial tree)
8
Биномиальное
дерево B0
(n = 20 = 1)
Биномиальное
дерево B1
(n = 21 = 2)
Биномиальное
дерево B2
(n = 22 = 4)
Биномиальное
дерево B3
(n = 23 = 8)
B3
B0B1
B0
B2
B0B1
B0
B2
B0B1
B0
B1
B0
B0
 Максимальная степень узла
в биномиальном дереве с n вершинами равна O(logn)
Биномиальная куча (Binomial heap)
9
 Биномиальная куча (Binomial heap) –
это множество биномиальных деревьев, которые
удовлетворяют свойствам биномиальных куч:
1) каждое биномиальное дерево упорядочено (ordered)
в соответствии со свойствами неубывающей
или невозрастающей кучи (min-heap/max-heap):
ключ узла не меньше/не больше ключа его родителя
2) для любого целого k ≥ 0 имеется не более одного
биномиального дерева, чей корень имеет степень k
 Биномиальная куча содержащая n узлов состоит
не более чем из log(𝑛) + 1 биномиальных деревьев
Биномиальная куча (Binomial heap)
10
6
2914
38
8
1711
27
1
2512
18
10
Биномиальная куча из 13 узлов
(упорядоченные биномиальные деревья B0, B2 и B3)
Биномиальное
дерево B0
(n = 20 = 1)
Биномиальное
дерево B2
(n = 22 = 4)
Биномиальное
дерево B3
(n = 23 = 8)
Биномиальное дерево (Binomial tree)
11
 Пусть биномиальная куча содержит n узлов
 Если записать n в двоичной системе исчисления,
то номера ненулевых битов будут соответствовать
степеням биномиальных деревьев, образующих кучу
6
2914
38
8
1711
27
1
2512
18
10
Биномиальная куча из 13 узлов (деревья B0, B2 и B3)
1310 = 11012
Биномиальное дерево (Binomial tree)
12
 Корни биномиальных деревьев биномиальной кучи
хранятся в односвязном списке – списке корней (root list)
 В списке корней узлы упорядочены по возрастанию их
степеней (степеней корней биномиальных деревьев кучи)
6
2914
38
8
1711
27
1
2512
18
10
Биномиальная куча из 13 узлов (деревья B0, B2 и B3)
1310 = 11012
Head
Узел биномиальной кучи
13
 Каждый узел биномиальной кучи
(биномиального дерева) содержит следующие поля:
parent
key
value
degree
child sibling
o key – приоритет узла (вес, ключ)
o value – данные
o degree – количество дочерних узлов
o parent – указатель на родительский узел
o child – указатель на крайний левый
дочерний узел
o sibling – указатель на правый
сестринский узел
Представление биномиальных куч
14
10
3
29
0
14
1
38
0
8
2
17
0
11
1
27
0
1
2
25
0
12
1
18
0
10
0
Head
Биномиальная куча из 13 узлов (деревья B0, B2 и B3)
Поиск минимального узла (FindMin)
15
 В корне каждого биномиального дерева хранится
его минимальный/максимальный ключ
 Для поиска минимального/максимального элемента
в биномиальной куче требуется пройти по списку
из log(𝑛) + 1 корней
6
2914
38
8
1711
27
1
2512
18
10Head
Поиск минимального узла (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)
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)
Слияние куч (Union)
18
 Для слияния (union, merge) двух куч H1 и H2
в новую кучу H необходимо:
1. Слить списки корней H1 и H2 в один
упорядоченный список
2. Восстановить свойства биномиальной кучи H
 После слияния списков корней известно, что в куче H
имеется не более двух корней с одинаковой степенью
и они соседствуют
Слияние списков корней
19
6
4410
17
29
3148
50
3
37
18
8
2223
24
30
3245
55
H2
15
3328
41
7
25
12
H1
 Списки корней H1 и H2 упорядочены по возрастанию
степеней узлов
 Слияние выполняется аналогично слиянию упорядоченных
подмассивов в MergeSort (сортировке слиянием)
Слияние списков корней
20
6
4410
17
29
3148
50
3
37
18
8
2223
24
30
3245
55
15
3328
41
7
25
12
H
 Списки корней H1 и H2 упорядочены по возрастанию
степеней узлов
 Слияние выполняется аналогично слиянию упорядоченных
подмассивов в MergeSort (сортировке слиянием)
Слияние списков корней
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
Слияние куч (Union)
22
6
4410
17
29
3148
50
3
37
18
8
2223
24
30
3245
55
15
3328
41
7
25
12
H
Ситуация после слияния списков корней
 Свойство 1 биномиальной кучи выполняется
 Свойство 2 не выполняется: два дерева B0 и два дерева B1
B0 B0
B1 B1
B2 B4
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
Случай 3
24
6
4410
17
29
3148
50
3
37
18
8
2223
24
30
3245
55
15
3328
41
7
25
12
H
x.degree = next-x.degree ≠ next-x.sibling.degree
x.key ≤ next-x.key
 Деревья x и next-x связываются
 Узел next-x становится левым дочерним узлом x
x next-x
x.sibling = next-x.sibling
BinomialTreeLink(next-x, x)
next-x = x.sibling
Случай 3
25
6
4410
17
29
3148
50
3
3718 8
2223
24
30
3245
55
15
3328
41
7
25
12
H
x next-x
x.degree = next-x.degree ≠ next-x.sibling.degree
x.key ≤ next-x.key
Связывание биномиальных деревьев
26
function BinomialTreeLink(child, parent)
child.parent = parent
child.sibling = parent.child
parent.child = child
parent.degree = parent.degree + 1
end function
TTreeLink = O(1)
10
0
parent
child
z.child
Случай 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
Случай 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
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
Случай 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
Слияние биномиальных куч (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
Слияние биномиальных куч (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)
Слияние биномиальных куч (Union)
33
 Вычислительная сложность слияния двух
биномиальных куч в худшем случае равна O(log(n))
 Длина списка корней не превышает
log(n1) + log(n1) + 2 ≤ log(n) + 2 = O(log(n))
 Цикл while в функции BinomialHeapUnion выполняется
не более O(log(n)) раз
 На каждой итерации цикла указатель перемещается
по списку корней вправо на одну позицию или удаляется
один узел – это требует времени O(1)
Вставка узла (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)
Удаление минимального узла (DeleteMin)
35
1. В списке корней кучи H отыскиваем корень x
с минимальным ключом и удаляем x из списка корней
(разрывам связь)
2. Инициализируем пустую кучу Z
3. Меняем порядок следования дочерних узлов корня х
на обратный, у каждого дочернего узла устанавливаем
поле parent в NULL
4. Устанавливаем заголовок кучи Z на первый элемент
нового списка корней
5. Сливаем кучи H и Z
6. Возвращаем x
Удаление минимального узла (DeleteMin)
36
1
2512
18
16
3326
42
37
41 6
2914
38
8
1711
27
10
1328
77
H
 В списке корней кучи H отыскиваем корень x
с минимальным ключом и удаляем x из списка корней
(разрывам связь)
x
Удаление минимального узла (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
Удаление минимального узла (DeleteMin)
38
25 12
18
16
3326
42
37
41
6
2914
38
8
1711
27
10
1328
77
H
 Сливаем кучи H и Z
Z
Удаление минимального узла (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
Удаление минимального узла (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)
Уменьшение ключа (DecreaseKey)
41
1. Получаем указатель на узел x и изменяем у него ключ
(newkey <= x.key)
2. Проверяем значение ключа родительского узла,
если он меньше ключа x, то выполняем обмен ключей (и
данных), повторяем обмены пока не поднимемся до корня
текущего биномиального дерева
6
2914
38
8
175
27
1
2512
18
10Head
x
p
Уменьшение ключа (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)
Удаление узла (Delete)
43
function BinomialHeapDelete(h, x)
BinomialHeapDecreaseKey(h, x, -Infinity)
BinomialHeapDeleteMin(h)
end function
TDelete = O(logn)
Узел биномиального дерева
struct bmheap {
int key;
char *value;
int degree;
struct bmheap *parent;
struct bmheap *child;
struct bmheap *sibling;
};
44
Пример использования 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
Создание узла биномиального дерева
struct bmheap *bmheap_create(int key, char *value)
{
struct bmheap *h;
h = (struct bmheap *)malloc(sizeof(*h));
if (h != NULL) {
h->key = key;
h->value = value;
h->degree = 0;
h->parent = NULL;
h->child = NULL;
h->sibling = NULL;
}
return h;
}
46
Поиск минимального элемента
struct bmheap *bmheap_min(struct bmheap *h)
{
struct bmheap *minnode, *node;
int minkey = ~0U >> 1; /* INT_MAX */
for (node = h; node != NULL;
node = node->sibling)
{
if (node->key < minkey) {
minkey = node->key;
minnode = node;
}
}
return minnode;
}
47
Слияние биномиальных куч
struct bmheap *bmheap_union(struct bmheap *a, struct bmheap *b)
{
struct bmheap *h, *prevx, *x, *nextx;
h = bmheap_mergelists(a, b);
prevx = NULL;
x = h;
nextx = h->sibling;
while (nextx != NULL) {
if ((x->degree != nextx->degree) ||
(nextx->sibling != NULL &&
nextx->sibling->degree == x->degree))
{
/* Cases 1 & 2 */
prevx = x;
x = nextx;
}
48
Слияние биномиальных куч
else if (x->key <= nextx->key) {
/* Case 3 */
x->sibling = nextx->sibling;
bmheap_linktrees(nextx, x);
} else {
/* Case 4 */
if (prevx == NULL) {
h = nextx;
} else {
prevx->sibling = nextx;
}
bmheap_linktrees(x, nextx);
x = nextx;
}
nextx = x->sibling;
}
return h;
}
49
Слияние списков корней
struct bmheap *bmheap_mergelists(struct bmheap *a,
struct bmheap *b)
{
struct bmheap *head, *sibling, *end;
end = head = NULL;
while (a != NULL && b != NULL) {
if (a->degree < b->degree) {
sibling = a->sibling;
if (end == NULL) {
end = a;
head = a;
} else {
end->sibling = a; /* Add to the end */
end = a;
a->sibling = NULL;
}
a = sibling;
}
50
Слияние списков корней
else {
sibling = b->sibling;
if (end == NULL) {
end = b;
head = b;
} else {
end->sibling = b; /* Add to the end */
end = b;
b->sibling = NULL;
}
b = sibling;
}
}
51
Слияние списков корней
while (a != NULL) {
sibling = a->sibling;
if (end == NULL) {
end = a;
} else {
end->sibling = a;
end = a;
a->sibling = NULL;
}
a = sibling;
}
52
Слияние списков корней
while (b != NULL) {
sibling = b->sibling;
if (end == NULL) {
end = b;
} else {
end->sibling = b;
end = b;
b->sibling = NULL;
}
b = sibling;
}
return head;
}
53
Связывание деревьев
void bmheap_linktrees(struct bmheap *y,
struct bmheap *z)
{
y->parent = z;
y->sibling = z->child;
z->child = y;
z->degree++;
}
54
Вставка элемента в биномиальную кучу
struct bmheap *bmheap_insert(struct bmheap *h,
int key, char *value)
{
struct bmheap *node;
if ((node = bmheap_create(key, value)) == NULL)
return NULL;
if (h == NULL)
return node;
return bmheap_union(h, node);
}
55
Удаление минимального элемента
struct bmheap *bmheap_deletemin(struct bmheap *h)
{
struct bmheap *x, *prev, *xmin, *prevmin,
*child, *sibling;
int minkey = ~0U >> 1; /* INT_MAX */
/* Lookup and unlink min node */
x = h;
prev = NULL;
while (x != NULL) {
if (x->key < minkey) {
minkey = x->key;
xmin = x;
prevmin = prev;
}
prev = x;
x = x->sibling;
}
56
Удаление минимального элемента
if (prevmin != NULL)
prevmin->sibling = xmin->sibling;
else
h = xmin->sibling;
/* Reverse linked list */
child = xmin->child;
prev = NULL;
while (child != NULL) {
sibling = child->sibling;
child->sibling = prev;
prev = child;
child = sibling;
}
free(xmin);
return bmheap_union(h, prev);
}
57
Задания
58
 Прочитать о левосторонней очереди (Leftist heap)
 Прочитать о скошенной очереди (Skew heap)
 Прочитать о куче Бродала (Brodal heap)

More Related Content

Лекция 6: Биномиальные кучи (Binomial heaps)

  • 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
  • 8. Биномиальное дерево (Binomial tree) 8 Биномиальное дерево B0 (n = 20 = 1) Биномиальное дерево B1 (n = 21 = 2) Биномиальное дерево B2 (n = 22 = 4) Биномиальное дерево B3 (n = 23 = 8) B3 B0B1 B0 B2 B0B1 B0 B2 B0B1 B0 B1 B0 B0  Максимальная степень узла в биномиальном дереве с n вершинами равна O(logn)
  • 9. Биномиальная куча (Binomial heap) 9  Биномиальная куча (Binomial heap) – это множество биномиальных деревьев, которые удовлетворяют свойствам биномиальных куч: 1) каждое биномиальное дерево упорядочено (ordered) в соответствии со свойствами неубывающей или невозрастающей кучи (min-heap/max-heap): ключ узла не меньше/не больше ключа его родителя 2) для любого целого k ≥ 0 имеется не более одного биномиального дерева, чей корень имеет степень k  Биномиальная куча содержащая n узлов состоит не более чем из log(𝑛) + 1 биномиальных деревьев
  • 10. Биномиальная куча (Binomial heap) 10 6 2914 38 8 1711 27 1 2512 18 10 Биномиальная куча из 13 узлов (упорядоченные биномиальные деревья B0, B2 и B3) Биномиальное дерево B0 (n = 20 = 1) Биномиальное дерево B2 (n = 22 = 4) Биномиальное дерево B3 (n = 23 = 8)
  • 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 имеется не более двух корней с одинаковой степенью и они соседствуют
  • 19. Слияние списков корней 19 6 4410 17 29 3148 50 3 37 18 8 2223 24 30 3245 55 H2 15 3328 41 7 25 12 H1  Списки корней H1 и H2 упорядочены по возрастанию степеней узлов  Слияние выполняется аналогично слиянию упорядоченных подмассивов в MergeSort (сортировке слиянием)
  • 20. Слияние списков корней 20 6 4410 17 29 3148 50 3 37 18 8 2223 24 30 3245 55 15 3328 41 7 25 12 H  Списки корней H1 и H2 упорядочены по возрастанию степеней узлов  Слияние выполняется аналогично слиянию упорядоченных подмассивов в MergeSort (сортировке слиянием)
  • 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
  • 22. Слияние куч (Union) 22 6 4410 17 29 3148 50 3 37 18 8 2223 24 30 3245 55 15 3328 41 7 25 12 H Ситуация после слияния списков корней  Свойство 1 биномиальной кучи выполняется  Свойство 2 не выполняется: два дерева B0 и два дерева B1 B0 B0 B1 B1 B2 B4
  • 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
  • 24. Случай 3 24 6 4410 17 29 3148 50 3 37 18 8 2223 24 30 3245 55 15 3328 41 7 25 12 H x.degree = next-x.degree ≠ next-x.sibling.degree x.key ≤ next-x.key  Деревья x и next-x связываются  Узел next-x становится левым дочерним узлом x x next-x
  • 25. x.sibling = next-x.sibling BinomialTreeLink(next-x, x) next-x = x.sibling Случай 3 25 6 4410 17 29 3148 50 3 3718 8 2223 24 30 3245 55 15 3328 41 7 25 12 H x next-x x.degree = next-x.degree ≠ next-x.sibling.degree x.key ≤ next-x.key
  • 26. Связывание биномиальных деревьев 26 function BinomialTreeLink(child, parent) child.parent = parent child.sibling = parent.child parent.child = child parent.degree = parent.degree + 1 end function TTreeLink = O(1) 10 0 parent child z.child
  • 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
  • 46. Создание узла биномиального дерева struct bmheap *bmheap_create(int key, char *value) { struct bmheap *h; h = (struct bmheap *)malloc(sizeof(*h)); if (h != NULL) { h->key = key; h->value = value; h->degree = 0; h->parent = NULL; h->child = NULL; h->sibling = NULL; } return h; } 46
  • 47. Поиск минимального элемента struct bmheap *bmheap_min(struct bmheap *h) { struct bmheap *minnode, *node; int minkey = ~0U >> 1; /* INT_MAX */ for (node = h; node != NULL; node = node->sibling) { if (node->key < minkey) { minkey = node->key; minnode = node; } } return minnode; } 47
  • 48. Слияние биномиальных куч struct bmheap *bmheap_union(struct bmheap *a, struct bmheap *b) { struct bmheap *h, *prevx, *x, *nextx; h = bmheap_mergelists(a, b); prevx = NULL; x = h; nextx = h->sibling; while (nextx != NULL) { if ((x->degree != nextx->degree) || (nextx->sibling != NULL && nextx->sibling->degree == x->degree)) { /* Cases 1 & 2 */ prevx = x; x = nextx; } 48
  • 49. Слияние биномиальных куч else if (x->key <= nextx->key) { /* Case 3 */ x->sibling = nextx->sibling; bmheap_linktrees(nextx, x); } else { /* Case 4 */ if (prevx == NULL) { h = nextx; } else { prevx->sibling = nextx; } bmheap_linktrees(x, nextx); x = nextx; } nextx = x->sibling; } return h; } 49
  • 50. Слияние списков корней struct bmheap *bmheap_mergelists(struct bmheap *a, struct bmheap *b) { struct bmheap *head, *sibling, *end; end = head = NULL; while (a != NULL && b != NULL) { if (a->degree < b->degree) { sibling = a->sibling; if (end == NULL) { end = a; head = a; } else { end->sibling = a; /* Add to the end */ end = a; a->sibling = NULL; } a = sibling; } 50
  • 51. Слияние списков корней else { sibling = b->sibling; if (end == NULL) { end = b; head = b; } else { end->sibling = b; /* Add to the end */ end = b; b->sibling = NULL; } b = sibling; } } 51
  • 52. Слияние списков корней while (a != NULL) { sibling = a->sibling; if (end == NULL) { end = a; } else { end->sibling = a; end = a; a->sibling = NULL; } a = sibling; } 52
  • 53. Слияние списков корней while (b != NULL) { sibling = b->sibling; if (end == NULL) { end = b; } else { end->sibling = b; end = b; b->sibling = NULL; } b = sibling; } return head; } 53
  • 54. Связывание деревьев void bmheap_linktrees(struct bmheap *y, struct bmheap *z) { y->parent = z; y->sibling = z->child; z->child = y; z->degree++; } 54
  • 55. Вставка элемента в биномиальную кучу struct bmheap *bmheap_insert(struct bmheap *h, int key, char *value) { struct bmheap *node; if ((node = bmheap_create(key, value)) == NULL) return NULL; if (h == NULL) return node; return bmheap_union(h, node); } 55
  • 56. Удаление минимального элемента struct bmheap *bmheap_deletemin(struct bmheap *h) { struct bmheap *x, *prev, *xmin, *prevmin, *child, *sibling; int minkey = ~0U >> 1; /* INT_MAX */ /* Lookup and unlink min node */ x = h; prev = NULL; while (x != NULL) { if (x->key < minkey) { minkey = x->key; xmin = x; prevmin = prev; } prev = x; x = x->sibling; } 56
  • 57. Удаление минимального элемента if (prevmin != NULL) prevmin->sibling = xmin->sibling; else h = xmin->sibling; /* Reverse linked list */ child = xmin->child; prev = NULL; while (child != NULL) { sibling = child->sibling; child->sibling = prev; prev = child; child = sibling; } free(xmin); return bmheap_union(h, prev); } 57
  • 58. Задания 58  Прочитать о левосторонней очереди (Leftist heap)  Прочитать о скошенной очереди (Skew heap)  Прочитать о куче Бродала (Brodal heap)