Capitolo 5: Heap e code con priorità Heap e code con priorità Un heap può essere paragonato ad un albero binario i cui nodi contengono dei dati e sono identificati da chiavi, inoltre la struttura deve soddisfare le seguenti proprietà: • tutte le foglie dell’heap hanno altezza h o h-1 per un valore opportuno di h; • le foglie di altezza h-1 (se ce ne sono) sono “a destra” delle foglie di altezza h; • per ogni nodo, la chiave del nodo è minore o uguale delle chiave dei figli. • Facciamo un esempio grafico. Per semplificare la gestione dell’heap si usa di solito rappresentarlo con un vettore: in particolare se l’heap è formato da N nodi avremo un vettore a di N elementi. In questa nuova struttura ogni nodo di posizione i avrà il figlio sinistro nella posizione 2i e figlio destro nella posizione 2i+1. 50 Capitolo 5: Heap e code con priorità Il vettore inoltre avrà le seguenti proprietà: • un nodo di indice i avrà entrambi i figli se 2i+1 ≤ N • l’unico eventuale nodo che ha solo un figlio, il figlio sinistro, ha indice i tale che 2i+1 ≥ N e 2i ≤ N • gli altri nodi sono foglie. Da quanto detto è semplice capire che tutti i nodi da indice 1 ≤ i ≤ (N-1)/2 hanno due figli; i nodi con indice maggiore di N/2 sono foglie e se N è pari il nodo di indice N/2 ha solo un figlio. 1 2 3 4 5 6 7 8 9 10 11 12 Rappresentazione 3 7 8 25 22 12 11 48 30 39 36 18 vettore dell’heap mostrato in con alto. Un problema riscontrabile con l’heap è l’inserimento di un elemento che potrebbe violare le proprietà della struttura. Infatti se inseriamo un nodo a[i] potrebbe accadere che il padre di a[i] abbia ora chiave maggiore di a[i] o viceversa può succedere che il nodo inserito abbia chiave maggiore dei suoi due figli. Per risolvere questo inconveniente bisognerà far salire o scendere il nuovo nodo lungo l’albero in modo da inserirlo nella corretta posizione. Mostriamo graficamente come avviene questo procedimento. 51 Capitolo 5: Heap e code con priorità Inserimento di elemento nell’heap Rimozione di elemento dall’heap 52 Capitolo 5: Heap e code con priorità La seguente procedura consente di riaggiustare l’heap in caso di inserimento o rimozione di un elemento. type data=record chiave:integer; valore:integer; end; vettore=array[1..N] of data; procedure heapify(var a:vettore; i,j,r:integer); { - i : indice del nodo modificato - j : indice del primo elemento dell’heap - r : indice dell’ultimo elemento dell’heap } var temp:data; figlio:integer; begin temp:=a[i]; while (i >= j ) and (temp.key < a[ i div 2].key) do begin a[i]:=a[ i div 2]; i:=i div 2; end; a[i]:=temp; while (i <= r div 2) do begin figlio:=2*i; if (figlio+1 <= r) and (a[figlio+1].key < a[figlio].key) then figlio:=figlio+1; if (temp.key<=a[figlio].key) then exit {forza il ciclo e esci}; a[i]:=a[figlio]; i:=figlio; end; a[i]:=temp; end; 53 Capitolo 5: Heap e code con priorità Grazie a questa procedura possiamo facilmente aggiungere e rimuovere un elemento dall’heap, in particolare per aggiungerlo basta inserire il nuovo elemento nella posizione di indice N+1,come se fosse una foglia, e poi sarà la procedura a rimettere al posto giusto l’elemento. a[N+1]:=nuovo elemento; heapify(N,1,N+1); Per eliminare un elemento di indice i invece esso sarà sostituito dalla’elemento di posizione N, sarà decrementato il valore di N e quindi la procedura riordinerà l’heap. A[i]:=a[N]; heapify(i,1,N-1); 54 Capitolo 5: Heap e code con priorità Bibliografia - Alan Bertossi, Algoritmi e strutture di dati Casa editrice UTET Libreria 2004 55