heap concetti ed applicazioni
ADT coda di priorità operazioni getfirst restituisce l'elemento nella struttura con massima priorità deletefirst cancella l'elemento nella struttura con massima priorità insert inserisce nuovo elemento nella struttura giugno 2003 ASD - Heap 2
code di priorità la priorità è in genere espressa da un intero a priorità max corrisponde intero max o min numerose applicazioni sistemi operativi, algoritmi di scheduling, gestione di code ecc. implementazione più efficiente tramite heap giugno 2003 ASD - Heap 3
heap heap = catasta condizione di heap 1. albero binario perfettamente bilanciato ma non è un BST!! 2. tutte le foglie sono a sinistra 3. ogni nodo contiene una chiave (priorità) maggiore o eguale di quelle presenti negli eventuali figli non è una struttura ordinata le visite in ampiezza e in profondità non forniscono un ordinamento delle chiavi ogni sottoalbero è uno heap giugno 2003 ASD - Heap 4
heap? 89 67 68 89 67 68 66 65 66 67 66 65 66 1 66 66 5 4 64 1 2 3 4 5 6 89 67 67 67 67 66 65 67 67 67 67 1 giugno 2003 ASD - Heap 5
max- e min-heap la struttura definita è detta max-heap variante: min-heap ogni nodo contiene una chiave minore o eguale di quelle presenti negli eventuali figli 6 13 22 13 23 44 27 23 32 33 24 56 81 min-heap giugno 2003 ASD - Heap 6
operazioni su un (max-)heap insert chiave inserisce nuova chiave nello heap occorre mantenere la condizione di heap deletefirst cancella chiave con max priorità dallo heap occorre mantenere la condizione di heap getfirst restituisce la chiave con max priorità nello heap non modifica lo heap giugno 2003 ASD - Heap 7
rappresentazione degli heap tutte le rappresentazione usate per gli alberi binarie sono ammissibili rappresentazione collegata, eventualmente con puntatori figligenitore rappresentazione tramite array particolarmente efficiente giugno 2003 ASD - Heap 8
rappresentazione tramite array ogni nodo v è memorizzato in posizione p(v ) se v è la radice allora p(v )=0 se v è il figlio sinistro di u allora p(v )=2p(u )+1 se v è il figlio destro di u allora p(v )=2p(u )+2 1 43 89 67 68 66 65 21 5 66 67 4 64 89 67 68 66 65 66 67 1 43 21 5 4 64 giugno 2003 ASD - Heap 9 0 1 2 3 4 5 6 7 8 9 10 11 12
heap su array vantaggi grande efficienza in termini di spazio l occupazione può essere minima facilità di navigazione genitore i figli j j = 2i + 1, 2i + 2 figlio i genitore j j = (i 1) / 2 svantaggio implementazione statica possono essere necessari progressivi raddoppiamenti/dimezzamenti dell array di supporto giugno 2003 ASD - Heap 10
rappresentazione in Java public class Heap { public static final int DEFAULTCAPACITY = 50; protected int storage[], size; public Heap() { this(defaultcapacity); public Heap(int dim) { storage = new int[dim]; size = 0; // metodi giugno 2003 ASD - Heap 11
rappresentazione in Java/2 public boolean isleaf(int i) { return getleftindex(i) >= size; public boolean isroot(int i) { return i == 0; public boolean isempty() { return size == 0; public boolean isfull() { return size == storage.length; giugno 2003 ASD - Heap 12
rappresentazione in Java/3 private static int getleftindex(int i) { return 2 * i + 1; private static int getrightindex(int i) { return getleftindex(i) + 1; private static int getparentindex(int i) { return (i - 1) / 2; public String tostring() { // segue implementazione operazioni giugno 2003 ASD - Heap 13
algoritmi su heap operazioni getfirst insert deletefirst altri algoritmi Array2Heap conversione di un array in heap HeapSort ordinamento di un array sfruttando uno heap giugno 2003 ASD - Heap 14
getfirst l'elemento di priorità max è contenuto nella cella 0 dell array operazione di costo costante O(1) giugno 2003 ASD - Heap 15
insert 1. inserisci elemento alla fine dello heap 2. while (elemento non è radice) and (elemento > genitore(elemento)) 3. scambia elemento con genitore giugno 2003 ASD - Heap 16
insert/2 giugno 2003 ASD - Heap 17
insert/3 Algorithm insert(int k) { storage[size] = k; int i = size++; int j = getparentindex(i); while(!isroot(i)&&(storage[i]>storage[j])){ exchange(i, j); i = j; j = getparentindex(i); codice semplificato giugno 2003 ASD - Heap 18
deletefirst 1. sostituisci primo elemento con ultima foglia ed elimina ultima foglia 2. p = radice 3. while (p non è foglia) and (p < un figlio) 4. scambia p con il suo figlio maggiore giugno 2003 ASD - Heap 19
deletefirst/2 giugno 2003 ASD - Heap 20
deletefirst/3 public void deletefirst() { if(!isempty()) { storage[0] = storage[--size]; heapify(0); // ripristina heap giugno 2003 ASD - Heap 21
heapify operazione "di servizio" rende heap il sottoalbero avente radice nella cella di posto i attraverso una sequenza di scambi genitore figlio maggiore i due sottoalberi di i sono heap utile in deletefirst ed in altri contesti giugno 2003 ASD - Heap 22
heapify /codice Algorithm heapify(int i) { if(isleaf(i)) return; j = getmaxchildindex(i); if(storage[i] < storage[j]) { exchange(i, j); heapify(j); giugno 2003 ASD - Heap 23
costi proporzionali all altezza dello heap Θ(lg n ) sia per l inserimento sia per la cancellazione giugno 2003 ASD - Heap 24
heap e code di priorità una coda di priorità è un tipo astratto con le seguenti operazioni enqueue, inserimento in coda dequeue, estrazione dalla coda dell elemento avente priorità max la priorità è in genere espressa da un intero gli heap sono strutture di dati eccellenti per l implementazione di code di priorità giugno 2003 ASD - Heap 25
altre operazioni su heap Array2Heap dato un array di interi costruisce uno heap con quegli interi HeapSort dato un array di interi ordina l array in senso crescente giugno 2003 ASD - Heap 26
Array2Heap dato un array arr, lo trasforma in un array che rappresenta uno heap attraverso una opportuna permutazione degli elementi semplice algoritmo for(i = 1; i < arr.length; i++) insert(arr[i]); costo (sfruttando l approssimazione di Stirling del fattoriale): lnn! 1 n lgi lgn! ( n lnn n) ( n lgn) i = = = = Θ 1 ln2 ln2 giugno 2003 ASD - Heap 27
Array2Heap/2 l algoritmo di Floyd è invece basato sull idea di applicare l operazione heapify ad alberi binari in cui i figli della radice sono radici di heap vengono progressivamente resi heap (heapified) i sottoalberi aventi la radice nel penultimo livello, quindi quelli con radice nel terz ultimo livello ecc. strategia bottom-up giugno 2003 ASD - Heap 28
algoritmo di Floyd 0 1 2 3 4 5 6 7 8 9 10 11 12 66 5 4 67 23 64 45 21 89 68 67 39 33 66 5 4 67 23 64 45 21 89 68 67 39 33 66 5 4 67 23 64 21 89 68 67 39 33 45 giugno 2003 ASD - Heap 29
algoritmo di Floyd/2 66 5 4 66 5 4 67 68 64 45 89 68 64 45 21 89 23 67 39 33 21 67 23 67 39 33 66 5 64 66 89 64 89 68 39 45 67 68 39 45 21 67 23 67 4 33 giugno 2003 ASD - Heap 30 21 5 23 67 4 33
algoritmo di Floyd/3 89 68 64 67 67 39 45 21 5 23 66 4 33 0 1 2 3 4 5 6 7 8 9 10 11 12 89 68 64 67 67 39 45 21 5 23 66 4 33 giugno 2003 ASD - Heap 31
implementazione in Java protected Heap(int[] data) {// nuovo costruttore storage = data; size = data.length; for(int i = getparentindex(size-1); i >= 0; i--) heapify(i); public static Heap array2heap(int[] data) { return new Heap(data); giugno 2003 ASD - Heap 32
analisi algoritmo di Floyd caso peggiore: ogni chiamata di heapify fa il max numero di scambi supp. heap con n = 2 k - 1 nodi (albero binario completo di altezza k ) nell'ultimo livello ci sono (n +1)/2 foglie nel penultimo livello ci sono (n +1)/4 nodi nel terzultimo (n +1)/8 e così via n f = n i + 1 n f = n i + 1 giugno 2003 ASD - Heap 33
analisi algoritmo di Floyd/2 una chiamata di heapify su un nodo a livello i provoca al più k i scambi (op. dominanti) 1 scambio se i è penultimo livello, 2 se i è il terzultimo k-1 scambi se i=1 # max di scambi = (# nodi al penultimo livello) 1 + (# nodi al terzultimo livello) 2 + + (# nodi a livello 2) (k-2) + (# nodi a livello 1) (k-1), con k = lg(n+1) # max di scambi = ((n + 1) / 4) 1 + ((n + 1) / 8) 2 + + 2 (lg(n + 1) - 2) + 1 (lg(n + 1) - 1) giugno 2003 ASD - Heap 34
analisi algoritmo di Floyd/2 # max di scambi = log( n + 1) i = 2 n + 1 i 2 ( i 1) = = ( n + ( n + 1) 1)( log( n + 1) i = 2 log( n + 1) i = 2 i 1 i 2 i i 2 log( n + 1) i = 2 1 2 i ) giugno 2003 ASD - Heap 35
analisi algoritmo di Floyd/3 considerato che 3 i 2 = i = i 2 2 ne segue che log( n + 1) i = 2 1 2 i > 0 log( 1) 1 3 log( 1) 1 3 ( + 1) n + i < ( + 1)( n + n n ) < ( n + i = 2 i i = 2 i 2 2 2 2 # max di scambi = O(n ) 1) giugno 2003 ASD - Heap 36
applicazioni dell algoritmo di Floyd realizzazioni di operazioni nonstandard eliminazione di una chiave qualunque es.: kill di un processo dato il suo PID O(n ) per trovare la chiave, O(1) per eliminarla, O(n ) per ripristinare la condizione di heap con l algoritmo di Floyd ordinamento di un array HeapSort giugno 2003 ASD - Heap 37
HeapSort algoritmo per l ordinamento di un array arr basato su uno heap Algorithm HeapSort(array arr) { heap = Array2Heap(arr); for(i = n 1; i >= 0; i--) { max = heap.getfirst(); heap.deletefirst(); arr[i] = max; costo: O(n) + O(log n!) = O(n log n), tramite Stirling giugno 2003 ASD - Heap 38
HeapSort in Java public static void heapsort(int[] data) { Heap aheap = array2heap(data); for(int i = aheap.size - 1; i > 0; i--) { aheap.exchange(0, i); aheap.size--; aheap.heapify(0); giugno 2003 ASD - Heap 39
aspetti pratici codice della classe Heap esercizi costruire una classe MinHeap, estendendo Heap opportunamente usando per gli heap la rappresentazione collegata, quali costi si otterrebbero per le tre operazioni fondamentali? giugno 2003 ASD - Heap 40
approssimazione di Stirling ln (n!) n ln n n per n "grande" (cfr., ad es., http://mathworld.wolfram.com/stirlingsapproximation.html) giugno 2003 ASD - Heap 41