ADT albero binario completo Un albero binario completo è un albero binario in cui ogni livello, fino al penultimo, è completamente riempito. L'ultimo livello è riempito da sinistra a destra 1 nodo a b d h i Strutture Dati e l 2 nodi c f m n g livelli completamente riempiti: contengono il massimo numero possibile di nodi 4 nodi l'ultimo livello può contenere un numero di nodi inferiore al massimo possibile, ma deve essere riempito da sinistra a destra ADT albero binario completo L'ADT albero binario completo è un caso speciale dell'ADT albero binario a b d Metodi aggiuntivi: h i c e l f m g ultimo nodo n add(e): aggiunge e restituisce un nuovo nodo esterno (foglia) v che memorizzerà l'elemento e in modo tale che il risultante albero sia un albero binario completo avente v come ultimo nodo remove(): rimuove l'ultimo nodo dell'albero restituendo il suo elemento Strutture Dati ADT albero binario completo Relativamente all'operazione di add si possono verificare sostanzialmente due casi: CASO 1 (ultimo livello non pieno) a b d Strutture Dati a c e b d c e f ADT albero binario completo Relativamente all'operazione di add si possono verificare sostanzialmente due casi: CASO 2 (ultimo livello pieno) a a b d c e f b g d h Strutture Dati c e f g ADT albero binario completo Interfaccia public interface CompleteBinaryTree<E> extends BinaryTree<E> { public Position<E> add(E elem); public E remove(); } Strutture Dati ADT albero binario completo Implementazione con arraylist Tutti i nodi dell'albero binario completo sono memorizzati in un array list A a la radice è memorizzata all'indice 1 b d h i c e l f m per ogni nodo v memorizzato all'indice i: g - il suo figlio sinistro è memorizzato all'indice 2i - il suo figlio destro è memorizzato all'indice 2i + 1 n a b c d e f g h i l m n i 2i 2i+1 Strutture Dati l'ultimo nodo è memorizzato in A[n] ADT albero binario completo Implementazione con arraylist le operazioni di add e remove richiedono tempo O(1) a viene coinvolto solo l'ultimo elemento dell'arraylist b d h i c e l f m g n a b c d e f g h i l m n i 2i 2i+1 Strutture Dati ADT albero binario completo Implementazione con arraylist le operazioni di add e remove richiedono tempo O(1) a viene coinvolto solo l'ultimo elemento dell'arraylist b d h i c e l f m n g o a b c d e f g h i i Strutture Dati l m n o 2i 2i+1 ADT albero binario completo Implementazione con arraylist le operazioni di add e remove richiedono tempo O(1) a viene coinvolto solo l'ultimo elemento dell'arraylist b d h i c e l f m n g in caso venga usato un arraylist estensibile, si può dimostrare che le operazioni di add e remove richiedono O(1) in tempo ammortizzato o a b c d e f g h i i Strutture Dati l m n o 2i 2i+1 ADT albero binario completo Implementazione con arraylist public class ArrayListCompleteBinaryTree<E> implements CompleteBinaryTree<E> { protected IndexList<BTPos<E>> T; // indexed list of tree positions protected static class BTPos<E> implements Position<E> { E element; int index; // indice di questa posizione nell'array list public BTPos(E elt, int i) { element = elt; index = i; } public E element() { return element; } public int index() { return index; } public E setElement(E elt) { E temp = element; element = elt; return temp; } public String toString() { return("[" + element + "," + index + "]"); } }... Strutture Dati ADT albero binario completo Implementazione con arraylist public ArrayListCompleteBinaryTree() { T = new ArrayIndexList<BTPos<E>>(); T.add(0, null); // the location at rank 0 is deliberately empty } /** Returns the number of (internal and external) nodes. */ public int size() { return T.size() - 1; } /** Returns whether the tree is empty. */ public boolean isEmpty() { return (size() == 0); } /** Returns whether v is an internal node. */ public boolean isInternal(Position<E> v) throws InvalidPositionException { return hasLeft(v); // if v has a right child it will have a left child } /** Returns whether v is an external node. */ public boolean isExternal(Position<E> v) throws InvalidPositionException { return !isInternal(v); } /** Returns whether v is the root node. */ public boolean isRoot(Position<E> v) throws InvalidPositionException { BTPos<E> vv = checkPosition(v); return vv.index() == 1; }... Strutture Dati ADT albero binario completo Implementazione con arraylist public boolean hasLeft(Position<E> v) throws InvalidPositionException { BTPos<E> vv = checkPosition(v); return 2*vv.index() <= size(); } /** Returns whether v has a right child. */ public boolean hasRight(Position<E> v) throws InvalidPositionException { BTPos<E> vv = checkPosition(v); return 2*vv.index() + 1 <= size(); } /** Returns the root of the tree. */ public Position<E> root() throws EmptyTreeException { if (isEmpty()) throw new EmptyTreeException("Tree is empty"); return T.get(1); } /** Returns the left child of v. */ public Position<E> left(Position<E> v) throws InvalidPositionException, BoundaryViolationException { if (!hasLeft(v)) throw new BoundaryViolationException("No left child"); BTPos<E> vv = checkPosition(v); return T.get(2*vv.index()); }... Strutture Dati ADT albero binario completo Implementazione con arraylist public Position<E> right(Position<E> v) throws InvalidPositionException { if (!hasRight(v)) throw new BoundaryViolationException("No right child"); BTPos<E> vv = checkPosition(v); return T.get(2*vv.index() + 1); } /** Returns the parent of v. */ public Position<E> parent(Position<E> v) throws InvalidPositionException, BoundaryViolationException { if (isRoot(v)) throw new BoundaryViolationException("No parent"); BTPos<E> vv = checkPosition(v); return T.get(vv.index()/2); } /** Returns an iterable collection of the children of v. */ public Iterable<Position<E>> children(Position<E> v) throws InvalidPositionException { PositionList<Position<E>> children = new NodePositionList<Position<E>>(); if (hasLeft(v)) children.addLast(left(v)); if (hasRight(v)) children.addLast(right(v)); return children; } Strutture Dati ADT albero binario completo Implementazione con arraylist public Iterable<Position<E>> positions() { PositionList<Position<E>> positions = new NodePositionList<Position<E>>(); for (int i =1; i < T.size(); i++) positions.addLast(T.get(i)); return positions; } /** Replaces the element at v. */ public E replace(Position<E> v, E o) throws InvalidPositionException { BTPos<E> vv = checkPosition(v); return vv.setElement(o); } /** Adds an element just after the last node (in a level numbering). */ public Position<E> add(E e) { int i = size() + 1; BTPos<E> p = new BTPos<E>(e,i); T.add(i, p); return p; } /** Removes and returns the element at the last node. */ public E remove() throws EmptyTreeException { if(isEmpty()) throw new EmptyTreeException("Tree is empty"); return T.remove(size()).element(); }... Strutture Dati ADT albero binario completo Esercizi Completare l'implementazione con i restanti metodi e aggiungere il metodo public Position<E> sibling(Position<E> v) che prende in input un nodo e restituisce il suo fratello. Strutture Dati Heap Un heap è un albero binario che memorizza una collezione di entrate nei suoi nodi e che soddisfa le due seguenti proprietà: 1) (proprietà di ordinamento) ogni nodo diverso dalla radice memorizza un elemento la cui chiave è maggiore o uguale di quella del suo padre; 2) (proprietà strutturale) in ogni livello dell'albero c'è il massimo numero di nodi possibile, tranne che nell'ultimo 3 che è riempito da sinistra a destra, cioè deve essere un albero binario completo. 5 7 Tutte le foglie sono addossate a sinistra 7 Strutture Dati 4 9 8 13 10 5 5 7 Altezza di un heap Qual è il massimo numero di nodi di un heap di altezza h? h 124...2 h=2h1 −1 Strutture Dati Altezza di un heap Qual è il minimo numero di nodi di un heap di altezza h? h-1 124...2 h−11=2 h−11=2h Strutture Dati Altezza di un heap Quindi: 2 h ≤ n ≤ 2 h1−1 ⇒ log n1−1 ≤ h ≤ log nh ⇒ h = ⌊log n⌋ 2 ≤n≤2 −1 implies log n1−1≤h≤l h Un heap con n entrate ha altezza h = ⌊log n⌋ Strutture Dati h1 h-1 ADT Coda a Priorità Interfaccia public interface PriorityQueue<K, V> { public int size(); public boolean isEmpty(); /** Restituisce senza rimuoverla una entry con chiave minima. */ public Entry<K,V> min() throws EmptyPriorityQueueException; /** Inserisce una coppia key-value e restituisce la entry creata. */ public Entry<K,V> insert(K key, V value) throws InvalidKeyException; /** Rimuove e restituisce una entry con chiave minima. */ public Entry<K,V> removeMin() throws EmptyPriorityQueueException; } Strutture Dati ADT Coda a Priorità Implementazione con heap L'ADT coda a priorità può essere implementato efficientemente con un heap. L'implementazione basata su heap consiste dei seguenti elementi: ● ● un heap, cioè un albero binario completo che memorizza nei suoi nodi entrate (chiave,valore) le cui chiavi soddisfano la proprietà di ordinamento dell'heap un comparatore, cioè un oggetto che definisce una relazione d'ordine totale tra le chiavi Strutture Dati ADT Coda a Priorità Implementazione con heap Inserimento di una nuova entrata con chiave 4 5 5 7 11 Strutture Dati 8 8 9 14 12 11 ADT Coda a Priorità Implementazione con heap Inserimento di una nuova entrata con chiave 4 1) eseguiamo prima un'operazione di add che preserva la proprietà di albero binario completo ma non quella di ordinamento 5 5 7 11 Strutture Dati 8 8 9 14 12 4 11 ADT Coda a Priorità Implementazione con heap Inserimento di una nuova entrata con chiave 4 1) eseguiamo prima un'operazione di add che preserva la proprietà di albero binario completo ma non quella di ordinamento 2) continuiamo a scambiare l'entrata del nuovo nodo z con quella di suo padre u fino a che chiave(z) ≥ chiave(u) 5 5 7 11 Strutture Dati 8 4 9 14 12 8 11 ADT Coda a Priorità Implementazione con heap Inserimento di una nuova entrata con chiave 4 1) eseguiamo prima un'operazione di add che preserva la proprietà di albero binario completo ma non quella di ordinamento 2) continuiamo a scambiare l'entrata del nuovo nodo z con quella di suo padre u fino a che chiave(z) ≥ chiave(u) 5 4 7 11 Strutture Dati 8 5 9 14 12 8 11 ADT Coda a Priorità Implementazione con heap Inserimento di una nuova entrata con chiave 4 1) eseguiamo prima un'operazione di add che preserva la proprietà di albero binario completo ma non quella di ordinamento 2) continuiamo a scambiare l'entrata del nuovo nodo z con quella di suo padre u fino a che chiave(z) ≥ chiave(u) 4 5 7 11 Strutture Dati 8 5 9 14 12 8 11 ADT Coda a Priorità Implementazione con heap Inserimento di una nuova entrata con chiave 4 nel caso pessimo si eseguono un numero di swap pari all'altezza dell'albero, cioè ⌊log n⌋ 4 5 7 11 Strutture Dati 8 5 9 14 12 8 11 ADT Coda a Priorità Implementazione con heap Rimozione (RemoveMin) 5 5 7 20 Strutture Dati 8 8 9 14 12 15 11 ADT Coda a Priorità Implementazione con heap Rimozione (RemoveMin) 1) copiamo l'entrata e dell'ultimo nodo nella radice 15 5 7 20 Strutture Dati 8 8 9 14 12 15 11 ADT Coda a Priorità Implementazione con heap Rimozione (RemoveMin) 1) copiamo l'entrata e dell'ultimo nodo nella radice 2) cancelliamo l'ultimo nodo 15 5 7 20 Strutture Dati 8 8 9 14 12 11 ADT Coda a Priorità Implementazione con heap Rimozione (RemoveMin) 1) copiamo l'entrata e dell'ultimo nodo nella radice 2) cancelliamo l'ultimo nodo 3) continuiamo a scambiare l'entrata e nel nodo u con quella del figlio di u avente la più piccola chiave fino a che la proprietà di ordinamento non verrà ristabilita 15 5 7 20 Strutture Dati 8 8 9 14 12 11 ADT Coda a Priorità Implementazione con heap Rimozione (RemoveMin) 1) copiamo l'entrata e dell'ultimo nodo nella radice 2) cancelliamo l'ultimo nodo 3) continuiamo a scambiare l'entrata e nel nodo u con quella del figlio di u avente la più piccola chiave fino a che la proprietà di ordinamento non verrà ristabilita 5 15 7 20 Strutture Dati 8 8 9 14 12 11 ADT Coda a Priorità Implementazione con heap Rimozione (RemoveMin) 1) copiamo l'entrata e dell'ultimo nodo nella radice 2) cancelliamo l'ultimo nodo 3) continuiamo a scambiare l'entrata e nel nodo u con quella del figlio di u avente la più piccola chiave fino a che la proprietà di ordinamento non verrà ristabilita 5 7 15 20 Strutture Dati 8 8 9 14 12 11 ADT Coda a Priorità Implementazione con heap Rimozione (RemoveMin) 1) copiamo l'entrata e dell'ultimo nodo nella radice 2) cancelliamo l'ultimo nodo 3) continuiamo a scambiare l'entrata e nel nodo u con quella del figlio di u avente la più piccola chiave fino a che la proprietà di ordinamento non verrà ristabilita 5 7 9 20 Strutture Dati 8 8 15 14 12 11 ADT Coda a Priorità Implementazione con heap Rimozione (RemoveMin) nel caso pessimo si eseguono un numero di swap pari all'altezza dell'albero, cioè ⌊log n⌋ 5 7 9 20 Strutture Dati 8 8 15 14 12 11 ADT Coda a Priorità Implementazione con heap Strutture Dati Operazione Complessità size, isEmpty O(1) min O(1) insert O(log n) removeMin O(log n)