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
124...2 h=2h1 −1
Strutture Dati
Altezza di un heap
Qual è il minimo numero di nodi di un heap di altezza h?
h-1
124...2 h−11=2 h−11=2h
Strutture Dati
Altezza di un heap
Quindi:
2 h ≤ n ≤ 2 h1−1
⇒ log n1−1 ≤ h ≤ log nh
⇒ h = ⌊log n⌋
2 ≤n≤2 −1
implies log n1−1≤h≤l
h
Un heap con n entrate ha altezza h = ⌊log n⌋
Strutture Dati
h1
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)