Heap binari e HeapSort Algoritmo SelectSort Invariante di ciclo: ad ogni passo • gli ultimi i elementi del vettore corrente sono gli i elementi massimi del vettore originale • gli ultimi i elementi del vettore corrente sono ordinati Inizializzazione: i = 0 Passo: estrarre l’elemento massimo dal vettore [1..n-i] e scambiarlo con quello in posizione n-i Condizione di arresto: i = n Pseudocodice di SelectSort SelectSort(n,A) For j = n downto 2 do {estrai il massimo e mettilo in coda} j_ max = FindMax(A,j) “scambia A[j_max] e A[j]” FindMax(A,j) i_max = 1 For i = 2 to j do If A[i_max] < A[i] then i_max = i return i_max SelectSort Scandisce la sequenza dall’ultimo elemento al primo Ad ogni iterazione (FindMax) • cerca l’elemento massimo A[j_max] nella sottosequenza corrente A[1,…,j] • scambia l’ultimo elemento con quello massimo (facendo uscire l’elemento massimo dalla sequenza e ricompattandola) j_max j n 1 disordinati ordinati Algoritmo di ordinamento stabile (perché?) Complessità O(n2) in ogni caso (anche in quello migliore) L’algoritmo di ordinamento HeapSort E’ una variante di SelectSort in cui si estrae l’elemento massimo in tempo costante grazie a uno heap Lo heap è un albero binario quasi completo (struttura), in cui le etichette dei nodi rispettano certe condizioni Albero binario: • insieme vuoto (albero vuoto o nullo) • unione di tre sottoinsiemi disgiunti – un nodo detto radice – un albero binario detto sottoalbero sinistro – un albero binario detto sottoalbero destro Alberi binari nodo radice sottoalbero destro sottoalbero sinistro Qualche definizione sugli alberi • Nodo foglia (o foglia) di un albero è un nodo i cui • • • • • sottoalberi sono vuoti (non ha figli) Nodo interno di un albero è un nodo che ha figli Percorso dal nodo i al nodo j è la sequenza di archi da attraversare per raggiungere il nodo j dal nodo i Grado di un nodo è il numero dei suoi figli Profondità di un nodo è il numero di archi del percorso da esso alla radice Altezza di un albero è la profondità massima dei suoi nodi Alberi Radice percorso dalla radice al nodo k Profondità 1 Profondità 0 Grado 2 Profondità 2 Grado 1 Profondità 3 Grado 0 k nodi interni nodi foglia Altezza 3 Alberi binari completi (e quasi) • Albero binario: tutti i nodi hanno grado 2 • Albero binario completo: – tutti i nodi interni hanno grado 2 – tutte le foglie hanno la stessa profondità • Albero binario quasi completo – tutte le foglie hanno profondità h o h-1 – tutti i nodi interni hanno grado 2, eccetto al più uno – se esiste un nodo di grado 1 + è a profondità h-1 + ha solo il figlio sinistro + i nodi alla sua destra (se esistono) sono foglie Alberi binari quasi completi profondità 0 nodo di grado 1 profondità h-1 profondità h nodi foglia Proprietà di uno heap Un albero heap è un albero binario quasi completo con etichette sui nodi, tali che per ogni nodo l’etichetta di entrambi i nodi figli non supera quella del padre. Condizione sulla struttura dell’albero Condizione sull’etichettatura dell’albero Un esempio di heap 45 34 28 30 14 25 21 15 22 16 12 20 La relazione d’ordine è fra padre e figli, non fra i due figli! Heap e ordinamenti parziali Uno heap rappresenta un ordinamento parziale, cioè una relazione tra elementi di un insieme • riflessiva: x è in relazione con se stesso xRx • antisimmetrica: se x è in relazione con y e y è in relazione con x, allora x = y xRyyRxx=y • transitiva: se x è in relazione con y e y è in relazione con z, allora x è in relazione con z xRyyRzxRz Esempi: le relazioni e (NON le relazioni > e < !) Heap e ordinamenti parziali Un albero binario di ricerca rappresenta un ordinamento totale: in un ordinamento totale per ogni coppia di elementi x e y vale o x R y o y R x Un ordinamento parziale è più debole di uno totale: possono esservi coppie di elementi privi di relazione Gli ordinamenti parziali modellano gerarchie con informazione incompleta o elementi con uguali valori Uno heap può essere implementato in vari modi; ad es., come un albero a puntatori, oppure come un array Rappresentazione di uno heap come array 45 1 34 28 2 3 30 25 22 12 4 5 6 7 14 21 15 16 20 8 9 10 11 12 1 2 3 4 5 6 7 8 9 10 11 12 45 34 28 30 25 22 12 14 21 15 16 20 Rappresentazione di uno heap come array • la radice dello heap sta nella prima posizione dell’array • se un nodo dello heap sta nella posizione i dell’array – il figlio sinistro sta nella posizione 2i – il figlio destro sta nella posizione 2i +1 • viceversa, un array A rappresenta uno heap quando A[i ] A[2i ] e A[i] A[2i+1] Rappresentazione di uno heap come array 45 1 34 28 2 3 30 25 22 12 4 5 6 7 14 21 15 16 20 8 9 10 11 12 1 2 3 4 5 6 7 8 9 10 11 12 45 34 28 30 25 22 12 14 21 15 16 20 Operazioni elementari su uno heap LeftSubtree(i) return 2i {restituisce la radice del sottoalbero sinistro} RightSubtree(i) return 2i + 1 {restituisce la radice del sottoalbero destro} Father (i) return i/2 {restituisce il nodo padre di i} Indichiamo con heapsize(A) n la dimensione dello heap • Heapify(A,i): ripristina la proprietà di heap nel sottoalbero radicato in i, assumendo che valga già per i sottoalberi destro e sinistro di i • BuildHeap(A): trasforma il generico array A in uno heap Heapify Dati due heap H1 e H2 con radici k >1 e k+1 Si possono fondere i due heap in un albero H con radice in posizione i = k/2 Se l’ elemento v in posizione A[i] non è v A[k] e v A[k+1], allora H non è uno heap. Per renderlo tale: si scambia A[i] con la maggiore fra le radici di H1 e H2 si applica Heapify ricorsivamente al sottoalbero selezionato (con la nuova radice v in posizione A[2i] o A[2i+1]) Algoritmo Heapify Heapify(A,i) l = LeftRoot(i) r = RightRoot(i) i_max = i If l heapsize(A) and A[l] > A[i_max] then i_max = l If r heapsize(A) and A[r] > A[i_max] then i_max = r If i_max i then “scambia A[i] e A[i_max]” Heapify(A,i_max) Un esempio di applicazione di Heapify i_max = i If l heapsize(A) and A[l] > A[i_max] then i_max = l If r heapsize(A) and A[r] > A[i_max] then i_max = r 45 1 A i 14 2 28 3 i_max 34 l 4 25 r 5 30 21 15 16 20 8 9 10 11 12 22 12 6 7 Un esempio di applicazione di Heapify If i_max i then “scambia A[i] e A[i_max]” ... 45 1 A i 14 34 2 28 3 i_max 34 14 l 4 25 r 5 30 21 15 16 20 8 9 10 11 12 22 12 6 7 Un esempio di applicazione di Heapify If i_max i then “scambia A[i] e A[i_max]” Heapify(A,i_max) 45 1 A 34 28 2 3 i 14 4 l 30 8 r 21 9 25 22 12 5 6 7 15 16 20 10 11 12 Un esempio di applicazione di Heapify i_max = i If l heapsize(A) and A[l] > A[i_max] then i_max = l If r heapsize(A) and A[r] > A[i_max] then i_max = r 45 1 A i_max l 30 8 34 28 2 3 i 14 4 r 21 9 25 22 12 5 6 7 15 16 20 10 11 12 Un esempio di applicazione di Heapify If i_max i then “scambia A[i] e A[i_max]” ... 45 1 A i_max l 14 30 8 34 28 2 3 i 14 30 4 r 21 9 25 22 12 5 6 7 15 16 20 10 11 12 Un esempio di applicazione di Heapify If i_max i then “scambia A[i] e A[i_max]” Heapify(A,i_max) 45 1 34 28 2 3 30 25 22 12 4 5 6 7 A i 14 21 15 16 20 8 9 10 11 12 Un esempio di applicazione di Heapify i_max = i If l heapsize(A) and A[l] > A[i_max] then i_max = l If r heapsize(A) and A[r] > A[i_max] then i_max = r 45 I test sono falsi l > heapsize(A) r > heapsize(A) per cui i_max = i (caso base) 1 34 28 2 3 30 25 22 12 4 5 6 7 A i 14 21 15 8 9 10 16 Heapify 20 termina! 11 Ora12 vale la proprietà heap Complessità di Heapify T(n) = max[O(1),O(1)+T(n’)] Heapify(A,i) l = LeftRoot(i) r = RightRoot(i) O(1) i_max = i If l heapsize(A) and A[l] > A[i_max] then i_max = l If r heapsize(A) and A[r] > A[i_max] then i_max = r If i_max i then O(1) “scambia A[i] e A[i_max]” Heapify(A,i_max) f(n) = T(n’) Complessità di Heapify Ad ogni chiamata ricorsiva, Heapify viene eseguito su un sottoalbero di n’ nodi dell’albero di n nodi n’ 2/3 n Sottoalbero completo di altezza h-1 Sottoalbero completo di altezza h-2 45 1 n’ 34 28 2 n’’ 3 30 25 22 12 4 5 6 7 14 21 15 16 8 9 10 11 n’/n è massimo Complessità di Heapify 45 1 n’ 34 28 2 n’’ 3 30 25 22 12 4 5 6 7 14 21 15 8 9 10 n’/n non è massimo (n’ è più piccolo!) Complessità di Heapify 45 1 n’ 34 28 2 n’’ 3 30 25 22 12 4 5 6 7 14 21 15 16 20 8 9 10 11 12 n’/n non è massimo (n è più grande!) Complessità di Heapify Sottoalbero completo di altezza h-1 Sottoalbero completo di altezza h-2 45 1 n’ 34 28 2 n’’ 3 30 25 22 12 4 5 6 7 14 21 15 16 8 9 10 11 n’ = 2h - 1 n’’ = 2h-1 - 1 n = 1 + n’ + n’’ = 32h-1 - 1 n’/n = 2h-1/(3 2h-1 -1) 2/3 Complessità di Heapify T(n) = max[O(1),max(O(1),O(1) + T(n’))] max[O(1),max(O(1),O(1) + T(2n/3))] T(2n/3) + (1) T(n) = O (log n) Sempre nel caso peggiore, T(n) T(n/3) + (1) T(n) = (log n) Heapify impiega tempo proporzionale all’altezza dell’albero T(n) = (log n) BuildHeap Trasforma l’array A in uno heap riordinando i suoi elementi attraverso l’algoritmo Heapify Heapify richiede che i due sottoalberi di ogni elemento siano già heap, ma gli ultimi n/2 elementi dell’array lo sono già, perché sono foglie, cioè radici di sottoalberi vuoti Quindi, basta inserire nello heap i primi n/2 elementi, usando Heapify per ripristinare la proprietà heap sui sottoalberi radicati in essi BuildHeap(A) For i = length(A)/2 downto 1 do Heapify(A,i) Un esempio di applicazione di BuildHeap i=6 1 2 3 4 5 6 7 8 9 10 11 12 14 45 28 34 15 20 12 30 21 25 16 22 14 1 45 28 2 3 34 15 20 12 4 5 6 7 30 21 25 16 22 8 9 10 11 12 BuildHeap(A) For i = length(A)/2 downto 1 do Heapify(A,i) Un esempio di applicazione di BuildHeap i=6 1 2 3 4 5 6 7 8 9 10 11 12 14 45 28 34 15 22 12 30 21 25 16 20 14 1 45 28 2 3 heap 34 15 22 20 12 4 5 6 7 30 21 25 16 22 20 8 9 10 11 12 BuildHeap(A) For i = length(A)/2 downto 1 do Heapify(A,i) Un esempio di applicazione di BuildHeap i=5 1 2 3 4 5 6 7 8 9 10 11 12 14 45 28 34 15 20 12 30 21 25 16 22 14 1 45 28 2 3 34 15 22 12 4 5 6 7 30 21 25 16 20 8 9 10 11 12 BuildHeap(A) For i = length(A)/2 downto 1 do Heapify(A,i) Un esempio di applicazione di BuildHeap i=5 1 2 3 4 5 6 7 8 9 10 11 12 14 45 28 34 25 20 12 30 21 15 16 22 14 heap 1 45 28 2 3 34 15 25 22 12 4 5 6 7 30 21 25 15 16 20 8 9 10 11 12 i=5 BuildHeap(A) For i = length(A)/2 downto 1 do Heapify(A,i) Un esempio di applicazione di BuildHeap i=4 1 2 3 4 5 6 7 8 9 10 11 12 14 45 28 34 15 20 12 30 21 25 16 22 14 1 heap 45 28 2 3 34 25 22 12 4 5 6 7 30 21 15 16 20 8 9 10 11 12 BuildHeap(A) For i = length(A)/2 downto 1 do Heapify(A,i) Un esempio di applicazione di BuildHeap i=3 1 2 3 4 5 6 7 8 9 10 11 12 14 45 28 34 15 20 12 30 21 25 16 22 14 1 45 28 heap 2 3 34 25 22 12 4 5 6 7 30 21 15 16 20 8 9 10 11 12 BuildHeap(A) For i = length(A)/2 downto 1 do Heapify(A,i) Un esempio di applicazione di BuildHeap i=2 1 2 3 4 5 6 7 8 9 10 11 12 14 45 28 34 15 20 12 30 21 25 16 22 14 1 heap 45 28 2 3 34 25 22 12 4 5 6 7 30 21 15 16 20 8 9 10 11 12 BuildHeap(A) For i = length(A)/2 downto 1 do Heapify(A,i) Un esempio di applicazione di BuildHeap i=1 1 2 3 4 5 6 7 8 9 10 11 12 14 45 28 34 15 20 12 30 21 25 16 22 14 1 45 28 2 3 34 25 22 12 4 5 6 7 30 21 15 16 20 8 9 10 11 12 BuildHeap(A) For i = length(A)/2 downto 1 do Heapify(A,i) Un esempio di applicazione di BuildHeap i=1 1 2 3 4 5 6 7 8 9 10 11 12 45 14 28 34 15 20 12 30 21 25 16 22 14 45 1 45 14 28 2 3 34 25 22 12 4 5 6 7 30 21 15 16 20 8 9 10 11 12 BuildHeap(A) For i = length(A)/2 downto 1 do Heapify(A,i) Un esempio di applicazione di BuildHeap i=1 1 2 3 4 5 6 7 8 9 10 11 12 45 34 28 14 15 20 12 30 21 25 16 22 45 14 1 34 28 2 3 34 14 25 22 12 4 5 6 7 30 21 15 16 20 8 9 10 11 12 BuildHeap(A) For i = length(A)/2 downto 1 do Heapify(A,i) Un esempio di applicazione di BuildHeap i=1 1 2 3 4 5 6 7 8 9 10 11 12 45 34 28 30 15 20 12 14 21 25 16 22 45 14 1 34 28 2 3 30 25 22 12 4 5 6 7 30 14 21 15 16 20 8 9 10 11 12 BuildHeap(A) For i = length(A)/2 downto 1 do Heapify(A,i) Un esempio di applicazione di BuildHeap i=1 1 2 3 4 5 6 7 8 9 10 11 12 45 34 28 30 15 20 12 14 21 25 16 22 45 14 1 heap 34 28 2 3 30 25 22 12 4 5 6 7 14 21 15 16 20 8 9 10 11 12 BuildHeap(A) For i = length(A)/2 downto 1 do Heapify(A,i) Complessità di BuildHeap BuildHeap chiama n/2 volte Heapify E’ allora TBH(n) = n/2 TH(n) = O(n log n)? Sì, ma si può stringere la stima sino a TBH(n) = O(n) Costruire uno heap di n elementi costa solo O(n)! Complessità di BuildHeap BuildHeap chiama Heapify • (n/2 volte su heap di altezza 0) (inutile eseguire) • n/4 volte su heap di altezza 1 • n/8 volte su heap di altezza 2 • … 14 h+1 • n/2 volte su heap di altezza h 1 45 28 2 3 34 15 20 12 4 5 6 7 30 21 25 16 22 8 9 10 11 12 Complessità di BuildHeap Poiché TH = O(log n) e h log n n n f (n) h1 O(h) O h 1 2 2 n h O h 2 h 0 2 log n log n h 1 h h 2 n 1/ 2 x , O(n) Poiché hx f (n) O 2 2 2 (1 1 / 2) (1 x) h 0 h HeapSort HeapSort è una variante di SelectSort che mantiene la sequenza in uno heap, facilitando così cui la ricerca dell’elemento massimo • si costruisce uno heap a partire dall’array non ordinato A • si scandisce l’array a partire dall’ultimo elemento e ad ogni passo • la radice A[1] (che è l’elemento massimo) viene scambiata con l’ultimo elemento dello heap corrente • si riduce di uno la dimensione corrente dello heap • si ripristina la proprietà heap con Heapify HeapSort e SelectSort SelectSort(n,A) For j = n downto 2 do j_ max = FindMax(A,j) “scambia A[j_max] e A[j]” HeapSort(A) BuildHeap(A) For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) { heapsize(A) = j } { j_ max = 1 } { ripristina lo heap, ridotto} Intuizioni su HeapSort n 1 disordinati costruisci heap max 1 parzialmente ordinati heap max i scambio + n Heapify 1 parzialmente ordinati heap n ordinati Un esempio di esecuzione di HeapSort HeapSort(A) BuildHeap(A) … 45 14 1 45 34 28 2 3 30 34 25 15 22 20 12 4 5 6 7 14 30 21 15 25 16 20 22 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j = 12 45 14 20 1 34 45 28 2 3 30 34 25 15 22 20 12 4 5 6 7 14 30 21 15 25 16 20 22 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j = 12 45 14 20 1 34 45 28 2 3 30 34 25 15 22 20 12 4 5 6 7 14 30 21 15 25 16 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j = 12 45 14 20 34 1 34 45 30 28 2 3 21 30 34 25 15 22 20 12 4 5 6 7 14 30 20 21 15 25 16 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j = 11 45 14 16 20 34 1 30 28 2 3 21 25 15 22 20 12 4 5 6 7 14 30 20 15 25 16 34 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j = 11 45 14 16 20 34 1 30 28 2 3 21 25 15 22 20 12 4 5 6 7 14 30 20 15 25 34 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j = 11 30 45 14 20 16 34 1 25 30 28 2 3 21 25 15 16 22 20 12 4 5 6 7 14 30 20 15 25 34 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j = 10 30 15 1 25 28 2 3 21 16 22 20 12 4 5 6 7 14 30 20 15 30 34 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j = 10 30 15 1 25 28 2 3 21 16 22 20 12 4 5 6 7 14 30 20 30 34 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j = 10 28 30 15 1 25 22 28 2 3 21 16 15 22 20 12 4 5 6 7 14 30 20 30 34 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =9 30 28 20 1 25 22 28 2 3 21 16 15 20 12 4 5 6 7 14 30 28 20 30 34 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =9 20 1 25 22 28 2 3 21 16 15 20 12 4 5 6 7 14 30 28 30 34 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =9 20 25 1 25 21 22 28 2 3 21 20 16 15 20 12 4 5 6 7 14 30 28 30 34 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =8 25 14 1 21 22 28 2 3 20 16 15 20 12 4 5 6 7 25 14 30 28 30 34 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =8 25 14 1 21 22 28 2 3 20 16 15 20 12 4 5 6 7 25 28 30 34 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =8 25 14 22 1 21 22 15 28 2 3 20 16 15 14 20 12 4 5 6 7 25 28 30 34 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =7 22 12 1 21 15 2 3 20 16 14 22 12 4 5 6 7 25 28 30 34 45 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =7 12 1 21 15 2 3 20 16 14 4 5 6 22 25 28 30 34 45 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =7 12 21 1 20 21 15 3 2 12 20 4 16 14 5 6 22 25 28 30 34 45 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =6 21 14 1 15 20 3 2 12 20 4 16 21 14 5 6 22 25 28 30 34 45 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =6 21 14 1 15 20 3 2 12 16 4 5 21 22 25 28 30 34 45 6 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =6 21 14 20 1 15 20 16 3 2 12 16 14 4 5 21 22 25 28 30 34 45 6 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =5 14 20 1 16 15 2 3 12 20 14 4 5 21 22 25 28 30 34 45 6 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =5 14 20 1 16 15 2 3 12 4 20 21 22 25 28 30 34 45 5 6 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =5 14 16 20 1 16 14 15 2 3 12 4 20 21 22 25 28 30 34 45 5 6 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =4 16 12 1 14 15 2 3 12 16 4 20 21 22 25 28 30 34 45 5 6 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =4 16 12 1 14 15 2 3 16 20 21 22 25 28 30 34 45 4 5 6 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =4 16 12 15 1 14 15 12 2 3 16 20 21 22 25 28 30 34 45 4 5 6 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =3 15 12 1 14 15 12 2 3 16 20 21 22 25 28 30 34 45 4 5 6 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =3 15 12 1 14 2 15 16 20 21 22 25 28 30 34 45 3 4 5 6 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =3 15 14 12 1 14 12 2 15 16 20 21 22 25 28 30 34 45 3 4 5 6 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =2 14 12 1 14 12 2 15 16 20 21 22 25 28 30 34 45 3 4 5 6 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =2 12 1 14 15 16 20 21 22 25 28 30 34 45 2 3 4 5 6 7 8 9 10 11 12 Un esempio di esecuzione di HeapSort HeapSort(A) … For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) j =2 L’array A ora è ordinato! 12 14 15 16 20 21 22 25 28 30 34 45 1 2 3 4 5 6 7 8 9 10 11 12 Invariante di ciclo per HeapSort Invariante di ciclo: ad ogni passo • gli ultimi i elementi del vettore corrente sono gli i elementi massimi del vettore originale • gli ultimi i elementi del vettore corrente sono ordinati • i primi n-i elementi del vettore corrente formano uno heap Inizializzazione: i = 0 e BuildHeap() Passo: estrarre l’elemento massimo dal vettore [1..n-i] e scambiarlo con quello in posizione n-i e recuperare la proprietà heap nel vettore [1..n-i-1] Condizione di arresto: i = n Complessità di HeapSort HeapSort(A) BuildHeap(A) For j = n downto 2 do “scambia A[1] e A[j]” Heapify(A[1,…,j-1],1) O(n) O(1) O(log n) Nel caso peggiore HeapSort chiama • 1 volta BuildHeap • n-1 volte Heapify sullo heap corrente THS(n) = max[O(n), (n-1) max(O(1), TH(n))] = max[O(n), max(O(n), O(n log n))] = O(n log n) Conclusioni su HeapSort • E’ un algoritmo di ordinamento sul posto per confronto e impiega tempo O(n log n) • Non è un algoritmo elementare • Sfrutta le proprietà della struttura dati astratta heap. • Dimostra che una buona rappresentazione dei dati spesso facilita il progetto di buoni algoritmi