Kolejka priorytetowa

by Jerry Sky



Zakładamy tutaj, że im mniejszy klucz tym element ma wyższy priorytet.

MakeQueue(A)(A)

Tworzy kolejkę priorytetową z tablicy AA elementów posiadających priorytety.

Insert(Q,key)(Q, \mathrm{key})

Wstawia element o kluczu key\mathrm{key} do kolejki QQ.

Minimum(Q)(Q)

Zwraca element należący do QQ o najmniejszym kluczu, czyli najwyższym priorytecie (nie usuwa go z QQ)

ExtractMin(Q)(Q)

Wykonuje Minimum(Q)(Q), a następnie usuwa zwrócony element z QQ.

DecreaseKey(Q,i)(Q,i)

Informuje kolejkę, że element Q[i]Q[i] ma zmniejszony klucz (zwiększony priorytet), może to skutkować przesunięciem elementu Q[i]Q[i] w kolejce.

Union(Q1,Q2)(Q_1, Q_2)

Zwraca kolejkę będącą złączeniem kolejek Q1Q_1 oraz Q2Q_2.

Delete(Q,i)(Q,i)

Usuwa element Q[i]Q[i] z kolejki QQ.

Implementacja kolejki przy pomocy kopca binarnego

MakeQueue(A)(A) (Binary heap)

Procedura ta może zostać zaimplementowana przy użyciu procedury BuildHeap(A)(A), która ma złożoność obliczeniową O(n)O(n), gdzie nn jest długością tablicy AA.

Insert(Q,key)(Q,\mathrm{key}) (Binary heap)

Procedura ta będzie dodawać nowy element w pierwszym możliwym miejscu (nowy liść), a następnie idąc w górę kopca (działając podobnie do Heapify, ale od dołu do góry) będzie szukane, gdzie umiejscowiony zostanie nowy element.

  1. Q.sizeQ.\mathrm{size}++
  2. i=Q.sizei = Q.\mathrm{size}
  3. while i>1Q[parent(i)]>keyi > 1 \land Q[\mathrm{parent}(i)] > \mathrm{key}:
    1. Q[i]=Q[parent(i)]Q[i] = Q[\mathrm{parent}(i)]
    2. i=parent(i)i = \mathrm{parent}(i)
  4. Q[i]=keyQ[i] = \mathrm{key}

Złożoność obliczeniowa: nowy klucz będzie przesuwany w górę kopca, wykonując O(1)O(1) operacji na każdym jego poziomie.
Zatem wiedząc, że wysokość kopca o nn elementach to O(lgn)O(\lg n) wiemy, że złożoność obliczeniowa tej procedury wynosi O(lgn)O(\lg n)

Minimum(Q)(Q) (Binary heap)

return Q[1]Q[1]

Kopiec jest zapisywany w tablicy QQ, zakładając, że jest to kopiec minimalny, jego pierwszy element będzie miał najmniejszy klucz. Złożoność obliczeniowa to Θ(1)\Theta(1).

ExtractMin(Q)(Q) (Binary heap)

  1. if Q.size<1Q.\mathrm{size} < 1:
    1. return „empty queue”\text{„empty queue”}
  2. else:
    1. min=Q[1]\min = Q[1]
    2. Q[1]=Q[Q.size]Q[1] = Q[Q.\mathrm{size}]
    3. Q.sizeQ.\mathrm{size}--
    4. Heapify(Q,1)(Q,1)
    5. return min\min

Jeśli kopiec nie jest pusty, zapisujemy wartość w jego korzeniu, zastępujemy ją ostatnią wartością z kolejki QQ, zmniejszamy rozmiar kolejki i przywracamy własność kopca.

Złożoność obliczeniowa: wszystkie operacje poza Heapify(Q,1)(Q,1) mają złożoność Θ(1)\Theta(1), natomiast Heapify(Q,1)(Q,1) dla kolejki długości nn ma złożoność O(lgn)O(\lg n).
Zatem w sumie złożoność to O(lgn)O(\lg n)

DecreaseKey(Q,i)(Q,i)

  1. while i>1Q[parent(i)]>Q[i]i > 1 \land Q[\mathrm{parent}(i)] > Q[i]:
    1. Q[i]=Q[parent(i)]Q[i] = Q[\mathrm{parent}(i)]
    2. i=parent(i)i = \mathrm{parent}(i)

Niniejsza procedura jest bardzo podobna do procedury Insert(Q,key(Q,\mathrm{key}. Jest realizowana poprzez sprawdzenie, czy własność kopca binarnego jest zachowana, jeśli nie to naprawia kopiec, przesuwając odpowiedni element w górę kopca.

Podobne wnioskowanie jak w przypadku Insert daje nam złożoność obliczeniową O(lgn)O(\lg n).

Union(Q1,Q2)(Q_1, Q_2) (Binary heap)

  1. Q=Q = Join(Q1,Q2)(Q_1, Q_2)  ~\triangleright złączenie elementów dwóch tablic
  2. BuildHeap(Q)(Q)

Złączenie kopców polega na przepisaniu obu kopców do jednej tablicy oraz wykonaniu procedury budowy kopca na nowej tablicy.

Złożoność obliczeniowa: zarówno połączenie dwóch tablic Q1,Q2Q_1, Q_2 jak i procedura BuildHeap(Q1,Q2)(Q_1, Q_2) możemy ograniczyć asymptotycznie przez złożoność liniową od sumarycznej wielkości tych tablic. Zakładając, że Q1.size+Q2.size=nQ_1.\mathrm{size} + Q_2.\mathrm{size} = n dostajemy złożoność O(n)O(n).

Delete(Q,i)(Q,i)

  1. Q[i]=Q[Q.size]Q[i] = Q[Q.\mathrm{size}]
  2. Q.sizeQ.\mathrm{size}--
  3. Heapify(Q,i)(Q,i)

Usuwanie elementu o indeksie ii z kopca polega na przesunięciu tego elementu do części tablicy, które będzie poza kopcem, zmniejszeniem wielkości kopca oraz naprawieniem własności kopca.

Złożoność obliczeniowa: operacja o największej złożoności obliczeniowej jest procedura Heapify(Q,i)(Q,i), która ma złożoność O(lgn)O(\lg n), zakładając, że n=Q.sizen = Q.\mathrm{size}.

Różne implementacje

Porównanie złożoności obliczeniowych procedur na kolejkach priorytetowych dla różnych implementacji kopców:

Procedura Kopiec binarny (koszt pesymistyczny) Kopiec dwumianowy (koszt pesymistyczny) Kopiec Fibonacciego (koszt zamortyzowany)
Insert O(lgn)O(\lg n) O(lgn)O(\lg n) O(1)O(1)
Minimum O(1)O(1) O(lgn)O(\lg n) O(1)O(1)
ExtractMin O(lgn)O(\lg n) O(lgn)O(\lg n) O(lgn)O(\lg n)
DecreaseKey O(lgn)O(\lg n) O(lgn)O(\lg n) O(1)O(1)
Union O(n)O(n) O(lgn)O(\lg n) O(1)O(1)
Delete O(lgn)O(\lg n) O(lgn)O(\lg n) O(lgn)O(\lg n)

More