Heap
Ordinamento e code di priorità
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Heap: definizione
Definizione. Uno Heap (binario) è un albero binario finito i cui vertici sono
etichettati da elementi di un insieme linearmente ordinato (chiavi), che soddisfa:
1.
2.
Se v è figlio di v' allora chiave(v) ≤ chiave(v');
L’albero è completo salvo al più l’ultimo livello, che è riempito da
sinistra.
Uno heap binario può essere realizzato come un vettore H di dimensione pari
alla cardinalità dell’albero e tale che:
• H[1] sia la radice dell’albero
• H[2i] e H[2i+1] siano rispettivamente il figlio sinistro e quello destro di H[i].
Ne segue che l’elemento la cui etichetta è massima tra tutte quelle dei vertici
dell’albero, è la radice ossia H[1].
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Heap: esempio
16
9
14
7
3
4
2
H=
16
1
8
10
1
9 14
7
4
8 10
3
2
2
4
5
6
8
9 10
3
7
1
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Left, Right, Parent
Per muoversi all’interno di uno heap rappresentato come un vettore sono utili
le seguenti funzioni:
// fissato uno heap h e la sua dimensione HeapSize(h)
Parent(i) = i / 2
// se i ≠ 0
Left(i) = if 2i < HeapSize(h) then 2i else i
Right(i) = if 2i+1 < HeapSize(h) then 2i+1 else i
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Inserimento2
Per inserire un nuovo elemento in uno heap, Insert lo aggiunge come foglia
dell’albero; quindi la fa risalire lungo il ramo cui è stato aggiunto sinché non
sia ricostruito lo heap.
Insert (h: heap; x: key)
HeapSize(h) := HeapSize(h)+1
i := HeapSize(h)
h[i] = x
while i > 1 and h[Parent(i)] < h[i] do
exchange(h[i], h[Parent(i)])
i := Parent(i)
Nota: la complessità è dell’ordine della lunghezza del ramo ossia O(lg n)
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Heap: Inserimento (1)
16
9
14
7
3
4
2
1
8
10
15
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Heap: Inserimento (2)
16
9
14
7
3
15
2
1
8
10
4
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Heap: Inserimento (3)
16
15
14
7
3
9
2
1
8
10
4
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Heap: Inserimento (4)
16
15
14
7
3
9
2
1
8
10
4
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Estrazione
L’estrazione da uno heap avviene dalla radice. Consta di due fasi:
1) l’elemento più a destra dell’ultimo livello rimpiazza la radice;
2) l’elemento ora in radice viene fatto discendere lungo l’albero finché non
sia maggiore di entrambi i figli; nel discendere si sceglie sempre il figlio col
valore massimo della chiave.
ExtractMaximum (h: heap)
h[1] := h[HeapSize(h)]
Fase (1)
HeapSize(h) := HeapSize(h) - 1
Heapify(h,1)
// Fase (2)
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Ricostruzione dello heap
La fase (2), di cui si incarica Heapify, è più generale, poiché questa funzione
può iniziare il suo lavoro in qualunque punto i dello heap, purché i sottoalberi
con radice in Left(i) e Right(i) siano a loro volta degli heap:
Heapify (h: heap; i: index)
largest := index of max{h[i],h[Left(i)],h[Right(i)]}
if largest ≠ i then
exchange(h[i],h[largest])
Heapify(h,largest)
Nota: se n è la cardinalità dell’albero con radice in i, allora Heapify è O(lg n)
(caso peggiore).
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Heap: Estrazione (1)
16
15
14
7
3
9
2
1
8
10
4
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Heap: Estrazione (2)
4
15
14
7
3
9
2
8
10
1
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Heap: Estrazione (3)
15
4
14
7
3
9
2
8
10
1
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Heap: Estrazione (4)
15
9
14
7
3
4
2
8
10
1
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Heap: Estrazione (5)
15
9
14
7
3
4
2
8
10
1
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Usi di uno heap
La struttura heap può essere impiegata per:
• implementare code di priorità;
• realizzare un algoritmo di ordinamento ottimo, HeapSort.
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Code di priorità (ADT)
Una coda di priorità è un insieme finito S di oggetti su cui è definita una
funzione priorità: S → Nat. Le operazioni definite su di essa sono illustrate nella
seguente specfica ADT:
datatype PriorityQueue, Element;
constructors: EmptyQueue: PriorityQueue
Insert: PriorityQueue, Element -> PriorityQueue
ExtractMaximum: PriorityQueue -> PriorityQueue
observations:
Maximum: PriorityQueue -> Element
semantics:
Insert(S,x) = S ∪ {x}
Maximum(S) = x tale che Priorità(x) =
max {Priorità(y)| y ∈ S}
ExtractMaximum(S) = S \ {Maximum(S)}
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Code di priorità: implementazione
Si possono implementare con gli heap: la funzione priorità si implementa
codificando ogni elemento come una coppia ⟨elemento, priorità⟩, e strutturando
lo heap in base alla seconda coordinata di ciascuna coppia (la chiave).
Le funzioni Insert e ExtractMaximum sono quelle viste; la funzione Maximum è
semplicemente:
Maximum (h: heap)
return h[1] // il massimo è sempre in radice
La funzione, non essendo distruttiva, non richiede infatti alcuna ricostruzione
dello heap, ed ha complessità O(1).
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Heapsort (1)
Si può sfruttare la struttura dati heap per costruire un algoritmo di ordinamento
simile, per struttura, al SelectSort con selezione del massimo.
1
i
n
V
V[1..i] è uno heap
V[i+1..n] è ordinato
∀ x ∈ V[1..i] ∀ y ∈ V[i+1..n]. x ≤ y
HeapSort(v: array)
BuildHeap(v) // riorganizza v in uno heap
for i := Length(v) downto 2 do
exchange(v[1],v[i])
HeapSize(v) := HeapSize(v) - 1
Heapify(v,1)
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
HeapSort (2)
BuildHeap. Se v[1..n] è un vettore qualsiasi, BuildHeap(v) lo riorganizza in
modo che sia uno heap. Pensando h[n/2 +1 .. n] come le foglie dell’albero
che diventerà uno heap, la condizione che Left(n/2 +1 ) e Right(n/2 +1 )
siano heap è trivialmente soddisfatta, onde possiamo iterare Heapify da n/2
a 1:
BuildHeap(v: array)
for i := length(v)/2 downto 1 do
Heapify(v,i)
Nota: un confine superiore alla complessità di BuildHeap è O(n lg n) (si
itera una procedura O(lg n) per ciascuno degli n vertici). Questa stima
grossolana si può raffinare sino a mostrare che in realtà BuildHeap è
lineare.
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Costruzione di uno Heap
2
6
18
3
42
44
94
12
55
79
2
6
18
3
42 12 55 44 94 79
1
2
3
4
5
6
7
8
9
10
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Costruzione di uno Heap
2
6
18
3
42
44
94
12
55
79
2
6
18
3
42 12 55 44 94 79
1
2
3
4
5
6
7
8
9
10
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Costruzione di uno Heap
2
6
18
3
79
44
94
12
55
42
2
6
18
3
79 12 55 44 94 42
1
2
3
4
5
6
7
8
9
10
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Costruzione di uno Heap
2
6
18
94
79
44
3
12
55
42
2
6
18 94 79 12 55 44
3
42
1
2
3
9
10
4
5
6
7
8
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Costruzione di uno Heap
2
6
55
94
79
44
3
12
18
42
2
6
55 94 79 12 18 44
3
42
1
2
3
9
10
4
5
6
7
8
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Costruzione di uno Heap
2
94
55
44
79
6
3
12
18
42
2
94 55 44 79 12 18
6
3
42
1
2
8
9
10
3
4
5
6
7
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Costruzione di uno Heap
94
79
55
44
42
6
3
12
2
94 79 55 44 42 12 18
1
18
2
3
4
5
6
7
6
3
2
8
9
10
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Heapsort (3)
A differenza del SelctSort, che è O(n2), HeapSort ha complessità O(n log n),
quindi è un algoritmo ottimo per l’ordinamento. Ciò si deve all’efficienza
della selezione del massimo nel semivettore sinistro, che ha complessità
logaritmica:
HeapSort(v: array)
BuildHeap(v) // O(n log n) (o meglio O(n))
for i := Length(v) downto 2 do // O(n) cicli
exchange(v[1],v[i])
HeapSize(v) := HeapSize(v) - 1
Heapify(v,1) // O(log n)
Allora: O(n) + O(n) O(lg n) = O(n) + O(n lg n) = O(n lg n).
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9
Riferimenti
Bertossi: Cap. 7
Cormen et alii.: Cap. 7
Ugo de' Liguoro - Algoritmi e Sperimentazioni 03/04 - Lez. 9