In questa lezione Costruire un max-heap [CLRS01] cap. 6 par. 6.3!1
Heapsort: analisi Heapsort(A) Build-Max-Heap(A) for i = A.length downto 2 do scambia A[1] e A[i] A.heap-size = A.heap-size - 1 Max-Heapify (A,1) n = A.length O(n) Θ(1) Θ(1) O(n log n) O(log n) Quindi, nel caso peggiore, per l Heapsort T(n) = O(n log n)!2
Paternità L heapsort è stato pubblicato da J. W. J. Williams nel 1964. Pochi mesi dopo Robert Floyd ne pubblicava una versione migliorata, in loco, e in cui propone la funzione che trasforma un array qualsiasi in un max-heap in O(n) che ora vedremo.!3
Robert Floyd Robert Floyd è anche noto per l algoritmo, chiamato appunto Floyd-Warshal, che trova tutti i più corti cammini in un grafo, per l algoritmo che trova i cicli in una sequenza e infine per la logica Floyd-Hoare per la verifica della correttezza dei programmi. E stato insignito del premio Turing nel 1978. (1936-2001)!4
Max-heapify Max-Heapify (A,i) Input: A è un array e i è un indice Prec: A[left(i)] e A[right(i)] sono radici di max-heap mentre A[i] può essere più piccolo dei suoi figli Output: A[i] è radice di un max-heap max = i if (2i +1 A.heap.size) then if (A[2i+1] > A[2i]) then max = 2i+1 else max = 2i (tra i due figli, se ci sono, si prende l indice del massimo) else if (2i A.heap-size) then max = 2i (al più c è il figlio sinistro) if (A[max] > A[i]) then scambia A[i] e A[max] Max-Heapify (A,max)!5
Costruire un Max-heap 25 3 1 6 Notiamo che in un albero 20 quasi completo la foglia più a destra sull ultimo livello è il nodo n-simo, 18 40 quindi suo padre, n/2, è l ultimo, da sinistra, nodo non foglia del livello precedente. Le foglie quindi sono i nodi di indice n/2 + 1,..., n. I nodi foglia sono banalmente dei max-heap, quindi ai loro padri si può applicare la Max-heapify, che li rende radici di max-heap.!6
Costruire un Max-heap 20 Max-Heapify sui nodi 1,3 e 20 3 1 18 40 25 6 40 25 6 18 20 3 1 I genitori di questi nodi, padri delle foglie, ora sono radici di un max heap Quindi di nuovo se un nodo ha i figli radici di un Max-heap si può applicare la Max-heapify,!7
Costruire un Max-heap Max-Heapify su 40 prima esecuzione del ciclo 25 6 18 20 3 1 25 40 Max-Heapify su seconda esecuzione del ciclo 3 1 6 18 20 25 40 6 18 20 3 1!8
Costruire un Max-heap Max-Heapify, su, prima 40 esecuzione del ciclo 25 6 18 20 3 1 25 40 Max-Heapify, su, seconda esecuzione del ciclo 3 25 1 6 3 40 18 20 1 6 18 20 Ora i figli della radice sono radici di Maxheap, quindi si può chiamare la Max- Heapify su!9
25 40 Costruire un Max-heap Max-Heapify, su, prima esecuzione del ciclo 6 18 20 40 3 1 25 Max-Heapify, su, seconda esecuzione del ciclo 40 3 1 6 18 20 25 20 6 18 3 1!
Build-Max-Heap: pseudocodice Build-Max-Heap (A)! Build-Max- Heap trasforma un array A,dato in input, in un max-heap. postc: A è un max-heap A.heapsize = A.length n = A.length for i = heapsize[a]/2 downto 1 do ogni nodo i+1,..., n è radice di un max-heap Max-Heapify (A,i) Partiamo da n/2, i nodi da n/2 +1 a n sono radici di max-heap. Quindi si applica la Max-Heapify risalendo nell albero dal nodo n/2 fino alla radice, percorrendo ogni livello da destra verso sinistra.!11
Build-Max-Heap: analisi Build-Max-Heap (A)! Build-Max- Heap trasforma un array A,dato in input, in un max-heap. postc: A è un max-heap A.heapsize = A.length n = A.length for i = heapsize[a]/2 downto 1 do ogni nodo i+1,..., n è radice di un max-heap Max-Heapify (A,i) Max-heapify ha una complessità O(h) se applicata a un nodo di altezza h, cioè a un nodo radice di un sotto albero di altezza h. Poiché la applichiamo n volte, maggiorando con lg n l altezza di ogni sotto albero si conclude che la complessità è O(n lg n). Ma il calcolo più preciso ci dà un risultato migliore!!12
Build-Max-Heap: analisi Per semplicità consideriamo un albero completo. Max-heapify ha una complessità O(h) se applicata a un nodo di altezza h. Applicandola a partire dal livello dei nodi padri delle foglie fino alla radice, notiamo che il numero dei nodi cui si applica la Max-Heapify si dimezza ogni volta che saliamo di livello mentre l altezza dell albero cui si applica cresce di 1. Questo suggerisce che tener conto dell altezza reale dei sotto alberi cui si applica la Max-Heapify può fare la differenza.!13
Build-Max-Heap (A) A.heapsize = A.length for i = heapsize[a]/2 downto 1 do Max- Heapify (A,i) Quante esecuzioni del ciclo in Max-heapify al più si fanno? Sia n = 2 h+1-1, ci sono 2 h foglie sulle quali non ci sono chiamate, 2 h-1 nodi di livello h-1, di altezza 1, sui quali il ciclo è eseguito al più 1 volta 2 h-2 nodi di livello h-2, di altezza 2, sui quali il ciclo è eseguito al più 2 volte 2 h-3 nodi di livello h-3, di altezza 3, sui quali il ciclo è eseguito al più 3 volte, 2 0 = 1 nodi di livello 0, di altezza h, sul quale sui quali il ciclo è eseguito al più h volte!14 2 14 8 3 16 7 9 8 4 6 2 5 7
Build-Max-Heap: analisi 1 Quindi il numero delle esecuzionioni del ciclo in totale è al più 1*2 h-1 + 2*2 h-2 + 3*2 h-3 + + h2 0 = 1*2 h /2 + 2*2 h /2 2 + 3*2 h /2 3 + + h2 h /2 h 2 h (1/2 + 2(1/2) 2 + 3(1/2) 3 + + h(1/2) h ) = O(2 h ), perché 1 k h k(1/2) k 0 k k(1/2) k = 2 = O(1) Poiché ogni singola esecuzione del ciclo ha un costo costante il costo totale è O(n) = O(2 h )!
Build-Max-Heap: analisi 2 Se l array ha un numero di nodi n tale che 2 h n 2 h+1-1, dove quindi h è l altezza dell albero quasi completo, possiamo concludere che il tempo di esecuzione è ancora O(n).!16
Altre operazioni Vedremo che con il Max-Heap si implementano in modo efficiente le operazioni di una coda di priorità. Quando si definisce una struttura dati, in genere si pensa anche a un costruttore, che spesso è una funzione di inserimento di un elemento nell insieme di base della struttura dati. Introduciamo la funzione di inserimento di un elemento in un Max_Heap.!17
Inserimento Poichè i nostri dati sono memorizzati in un array è giocoforza inserire il nuovo elemento in coda alla porzione di array che costituisce il Max-Heap, poi incrementarne la dimensione e fare in modo che l array sia ancora un Max-Heap dopo l inserimento.!18
Insert(A,x): passo 1 A= 16 14 8 7 9 3 2 4 1 4 0 7 1 2 3 4 5 6 7 8 9 11 12 13 Insert(A,) heap-size(a)= A= 16 14 8 7 9 3 2 4 1 0 7 14 16 1 2 3 4 5 6 7 8 9 11 12 13 heap-size(a)=11 8 7 9 3 2 4 1!19
Insert(A,x): passo 2 A= 16 14 8 7 9 3 2 4 1 0 7 1 2 3 4 5 6 7 8 9 11 12 13 Insert(A,) heap-size(a)=11 A= 16 8 14 9 3 2 4 1 7 0 7 14 16 1 2 3 4 5 6 7 8 9 11 12 13 heap-size(a)=11 8 7 9 3 2 4 1!20
Pseudocodice Insert(A,x) Max-Heap-insert(A,key) Prec: A è un max-heap e key una chiave possibile Postc: Inserisce key in A, ripristinando la proprietà del max-heap if A.heap.size = A.length then error l heap è pieno A.heap.size = A.heap.size+1 i = heap-size[a] A[i] = key while (i > 1 and A[i/2] < A[i]) scambia A[i/2] con A[i] i = i/2 Caso peggiore: Θ(lg n) n = heap-size(a)!21
Costruire un Max-Heap 1 In una struttura dati in cui si dispone di un metodo per inserire nuovi oggetti sembra naturale usarlo come costruttore: Costruisci-Max-Heap(A) input: A è un array output: A è un max-heap A.heapsize = 1 i = 2 while i A.length do A[1...i-1] è un maxheap. Max-Heap-insert(A,A[i])!22
Costruisci Max_Heap con insert A= 16 14 20 30 40 1 2 3 4 5 6 Insert(A,14) heap-size(a)=1 16 14 Insert(A,) 16 A= 16 14 20 30 40 1 2 3 4 5 6 heap-size(a)=2 A= 16 14 20 30 40 1 2 3 4 5 6 14 heap-size(a)=3!23
Costruisci Max_Heap con insert A= 16 14 20 30 40 1 2 3 4 5 6 Insert(A,20) heap-size(a)=3 14 16 A= 20 16 14 30 40 1 2 3 4 5 6 heap-size(a)=4 20!24
Costruisci Max_Heap con insert A= 20 16 14 30 40 1 2 3 4 5 6 heap-size(a)=4 20 16 14 30 30 A= Insert(A,30) 30 20 14 16 40 1 2 3 4 5 6 heap-size(a)=5 20 14 16!25
Costruisci Max_Heap con insert A= 30 20 14 16 40 1 2 3 4 5 6 Insert(A,40) heap-size(a)=5 20 30 A= 40 20 30 14 16 1 2 3 4 5 6 14 16 40 40 heap-size(a)=6 20 30 14 16!26
Complessità L inserimento di un elemento in un max-heap di altezza h costa O(h). Poiché inseriamo n elementi, maggiorando con lg n l altezza di ogni albero intermedio si conclude che la complessità è O(n lg n). Facendo un calcolo più dettagliato il risultato non cambia: per i 2 nodi di livello 1 l inserimento prende 2* 1c, per i 2 2 nodi di livello 2 si ha al più un costo 2 2 *2c per i 2 3 nodi di livello 3 si ha al più un costo 2 3 *3c per i 2 h nodi di livello h si ha la più un costo 2 h *hc!27
Conclusione calcolo Quindi bisogna valutare la somma: c(1*2 1 + 2*2 2 + 3*2 3 + + h2 h ) = c 1 i h i2 i = O(h2 h ) = O(n lg n) Quindi costruire un Max-Heap, a partire da un array qualunque di n elementi, utilizzando la funzione insert porta a un tempo di esecuzione O(n lg n), mentre utilizzando la Max-Heapify (A,i) si ottiene il risultato in O(n).!28