Лекция 7: Очереди с приоритетами. Бинарные кучи (пирамиды)
1. Лекция 7:
Очереди с приоритетами
Курносов Михаил Георгиевич
к.т.н. доцент Кафедры вычислительных систем
Сибирский государственный университет
телекоммуникаций и информатики
http://www.mkurnosov.net
3. Значение (Value) Приоритет (Priority)
Слон 3
Кит 1
Лев 15
Очередь с приоритетом (Priority Queue)
3
Очередь с приоритетом (Priority queue) – очередь, в которой
элементы имеют приоритет (вес)
Поддерживаемые операции:
o Insert(key, value) – добавление элемента в очередь
o DeleteMin/DeleteMax – удаляет из очереди элемент
с мин./макс. ключом
o Min/Max – возвращает элемент с мин./макс. ключом
o DecreaseKey – изменяет значение ключа элемента
o Merge(q1, q2) – сливает две очереди в одну
4. Двоичная куча (Binary Heap)
4
Двоичная куча (пирамида, сортирующее дерево,
binary heap) – это двоичное дерево, удовлетворяющее
следующим условиям:
a) приоритет (вес) любой вершины не меньше ( ≥ ),
приоритета потомков
b) дерево является полным двоичным деревом
(complete binary tree) – все уровни
заполнены, возможно
за исключением последнего
5. Двоичная куча (Binary heap)
5
max-heap
Приоритет любой вершины
не меньше (≥),
приоритета потомков
min-heap
Приоритет любой вершины
не больше (≤),
приоритета потомков
6. Реализация двоичной кучи на основе массива
6
2 4
8 7
1
9 3
14 10
16
max-heap (10 элементов)
16 14 10 8 7 9 3 2 4 1
Массив H[1..14]:
Корень дерева храниться вячейке H[1] – максимальный элемент
Индекс родителя узла i: Parent(i) = ݅/2
Индекс левого дочернего узла: Left(i) = 2i
Индекс правого дочернего узла: Right(i) = 2i + 1
H[Parent(i)] ≥ H[i]
7. Реализация двоичной кучи на основе массива
7
struct heapitem {
int priority; /* Приоритет элемента */
char *value; /* Данные */
};
struct heap {
int maxsize; /* Максимальный размер массива */
int nitems; /* Количество элементов в куче */
/* Элементы кучи (хранятся с индекса 1) */
struct heapitem *items;
};
17. for (k = 1, n = h->nitems - 1; 2 * k <= n;
k = j)
{
j = 2 * k;
if (j < n && h->items[j].priority <
h->items[j + 1].priority)
{
j++;
}
if (h->items[k].priority >=
h->items[j].priority)
{
break;
}
heap_swap(&h->items[k], &h->items[j]);
}
return h->items[h->nitems--];
}
Удаление максимального элемента
17TRemoveMax = O(logn)
18. for (k = 1, n = h->nitems - 1; 2 * k <= n;
k = j)
{
j = 2 * k;
if (j < n && h->items[j].priority <
h->items[j + 1].priority)
{
j++;
}
if (h->items[k].priority >=
h->items[j].priority)
{
break;
}
heap_swap(&h->items[k], &h->items[j]);
}
return h->items[h->nitems--];
}
Удаление максимального элемента
18
Прочитать в Sedgewick2001, с. 366-368
о функциях fixUp и fixDown
Эти функции составляют основу процедур
heap_insert и heap_removemax
19. int main()
{
struct heap *h;
struct heapitem item;
h = heap_create(20);
heap_insert(h, 10, “Купить хлеб”);
heap_insert(h, 9, “Заплатить за Интернет”);
heap_insert(h, 15, “Сходить в библиотеку”);
item = heap_removemax(h);
printf("Item: %dn", item.priority);
heap_free(h);
return 0;
}
Работа с двоичной кучей
19
20. Сортировка на базе двоичной кучи
20
На основе двоичной кучи можно реализовать алгоритм сортировки
с вычислительной сложностью O(nlogn) в худшем случае
Как ?
function HeapSort(v[1:n])
h = CreateBinaryHeap(n)
for i = 1 to n do
HeapInsert(h, v[i], v[i])
end for
for i = 1 to n do
v[i] = HeapRemoveMax(h)
end for
end function
T1 = O(n)
T2 = O(logn)
T3 = O(logn)
21. Сортировка на базе двоичной кучи
21
На основе двоичной кучи можно реализовать алгоритм сортировки
с вычислительной сложностью O(nlogn) в худшем случае
Как ?
function HeapSort(v[1:n])
h = CreateBinaryHeap(n)
for i = 1 to n do
HeapInsert(h, v[i], v[i])
end for
for i = 1 to n do
v[i] = HeapRemoveMax(h)
end for
end function
T1 = O(n)
T2 = O(logn)
T3 = O(logn)
T = T1 + nT2 + nT3 =
= n + nlogn + nlogn
= O(nlogn)
22. Очередь с приоритетом (Priority queue)
22
В таблице приведены трудоемкости операций очереди
с приоритетом (в худшем случае, 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)