Heap Ordinamento e code di priorità Ugo de Liguoro.

23
Heap Ordinamento e code di priorità Ugo de’ Liguoro

Transcript of Heap Ordinamento e code di priorità Ugo de Liguoro.

Page 1: Heap Ordinamento e code di priorità Ugo de Liguoro.

HeapOrdinamento e code di priorità

Ugo de’ Liguoro

Page 2: Heap Ordinamento e code di priorità Ugo de Liguoro.

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. Se v è figlio di v’ allora chiave(v) chiave(v’);

2. 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 è maggiore tra tutte quelle dei vertici dell’albero, è la radice ossia H[1].

Page 3: Heap Ordinamento e code di priorità Ugo de Liguoro.

Heap: esempio

16

9 14

7 4 8 10

3 2 1

16 9 14 7 4 8 10 3 2 1

1 2 3 4 5 6 7 8 9 10

H =

Page 4: Heap Ordinamento e code di priorità Ugo de Liguoro.

Left, Right, ParentPer 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

Page 5: Heap Ordinamento e code di priorità Ugo de Liguoro.

InserimentoPer 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(log n)

Page 6: Heap Ordinamento e code di priorità Ugo de Liguoro.

Heap: Inserimento (1)

16

9 14

7 4 8 10

3 2 1 15

Page 7: Heap Ordinamento e code di priorità Ugo de Liguoro.

Heap: Inserimento (2)

16

9 14

7

4

8 10

3 2 1

15

Page 8: Heap Ordinamento e code di priorità Ugo de Liguoro.

Heap: Inserimento (3)

16

9

14

7

4

8 10

3 2 1

15

Page 9: Heap Ordinamento e code di priorità Ugo de Liguoro.

Heap: Inserimento (4)

16

9

14

7

4

8 10

3 2 1

15

Page 10: Heap Ordinamento e code di priorità Ugo de Liguoro.

EstrazioneL’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)]

HeapSize(h) := HeapSize(h) - 1

Heapify(h,1) // Fase (2)

Fase (1)

Page 11: Heap Ordinamento e code di priorità Ugo de Liguoro.

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)

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:

Ricostruzione di uno heap

Nota: se n è la cardinalità dell’albero con radice in i, allora Heapify è O(log n) (caso peggiore).

Page 12: Heap Ordinamento e code di priorità Ugo de Liguoro.

Heap: Estrazione (1)

16

9

14

7

4

8 10

3 2 1

15

Page 13: Heap Ordinamento e code di priorità Ugo de Liguoro.

Heap: Estrazione (2)

4

9

14

7 8 10

3 2 1

15

Page 14: Heap Ordinamento e code di priorità Ugo de Liguoro.

Heap: Estrazione (3)

4

9

14

7 8 10

3 2 1

15

Page 15: Heap Ordinamento e code di priorità Ugo de Liguoro.

Heap: Estrazione (4)

4

9 14

7 8 10

3 2 1

15

Page 16: Heap Ordinamento e code di priorità Ugo de Liguoro.

Heap: Estrazione (5)

4

9 14

7 8 10

3 2 1

15

Page 17: Heap Ordinamento e code di priorità Ugo de Liguoro.

Usi di uno heap

La struttura heap può essere impiegata per:

• implementare code di priorità;

• realizzare un algoritmo di ordinamento ottimo, HeapSort.

Page 18: Heap Ordinamento e code di priorità Ugo de Liguoro.

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)}

Page 19: Heap Ordinamento e code di priorità Ugo de Liguoro.

Code di priorità: implementazione

Si possono implementare con gli heap: la funzione priorità si implementa codificando ogni elemento come una coppia elemento, priorità (usando le strutture del C), 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).

Page 20: Heap Ordinamento e code di priorità Ugo de Liguoro.

Heapsort (1)Si può sfruttare la struttura dati heap per costruire un algoritmo di ordinamento simile, per struttura, al SelectSort con selezione del massimo.

iV

1 n

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)

Page 21: Heap Ordinamento e code di priorità Ugo de Liguoro.

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 log n) (si itera una procedura O(log n) per ciascuno degli n vertici). Questa stima grossolana si può raffinare sino a mostrare che in realtà BuildHeap è lineare.

Page 22: Heap Ordinamento e code di priorità Ugo de Liguoro.

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(log n) = O(n) + O(n log n) = O(n log n).

Page 23: Heap Ordinamento e code di priorità Ugo de Liguoro.

Heap: fine.