Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Elettronica,
Informazione e
Bioingegneria
API 2013/4
Alberi
@ G. Gini 2013
Alberi
Alberi binari
Rappresentazioni
Propriet
Alberi n-ari
attraversamento
Alberi binari di ricerca
Bilanciamento
G. Gini 2013/4
G. Gini 2013/4
albero
Lalbero e un tipo astratto di dati usato
per rappresentare relazioni
gerarchiche.
- struttura del file system
- -albero genealogico
- organigramma
- albero di decisione
- .
G. Gini 2013/4
Albero sintattico
Input: s=6*8+((2+42)*(5+12)+987*7*123+15*54)
Output:
exp
term
fact
term
fact
fact
exp
term
term
fact
fact
exp
exp
term
term
term
term
fact
fact
fact
fact
+ 12
+ 42
fact
fact
+ 978 *
term
fact
fact
* 123 + 15
fact
54
parsing di espressioni
(9+7)*(3-(4+1))
notazione polacca inversa - postfissa(POSTORDER)
97+341+-*
G. Gini 2013/4
D
C
G. Gini 2013/4
G. Gini 2013/4
Profondit e altezza
Profondit = il numero di livelli
dellalbero
A ha profondit 0.
B e C sono a livello 1.
Altezza =
massima profondit + 1
Lalbero ha altezza 4.
0
1
2
3
4
altezza
G. Gini 2013/4
Alcune definizioni
Albero binario pieno (Full binary tree) - ogni nodo una
foglia oppure un nodo interno con 2 figli non vuoti
G. Gini 2013/4
Caso base: un full binary tree con 1 nodo interno ha due foglie
G. Gini 2013/4
G. Gini 2013/4
-1
11
Dimensionare larray 1D
profondit = il numero di livelli dellalbero, ovvero il numero di
archi da attraversare per andare dalla radice alla foglia pi
lontana (nellesempio 2)
7
/ \
3
9
/ \
\
1
5
11
Un albero binario pieno (full) di profondit n ha:
1 nodo radice + 2 nodi primo livello + 4 nodi di secondo livello +...
Quindi il numero dei nodi : 20+21++2n =
n
i=0
G. Gini 2013/4
Implementazione in array 2D
posizione
10
11
genitore
--
Figlio sinistro
11
--
--
--
--
--
--
Figlio destro
10
--
--
--
--
--
--
--
Fratello sinistro
--
--
--
--
--
--
--
Fratello destro
--
--
--
--
--
10
--
--
G. Gini 2013/4
Rappresentazione a puntatori
Vediamo lalbero binario come una lista di scatole di tre elementi:
linformazione, il puntatore al sottoalbero sinistro, il puntatore al
sottoalbero destro.
G. Gini 2013/4
nota
G. Gini 2013/4
Ricerca elemento
Restituisce NULL se non trova nellalbero t il dato d, altrimenti
restituisce il puntatore al primo nodo che lo contiene
visita t in preordine sinistro
G. Gini 2013/4
G. Gini 2013/4
G. Gini 2013/4
G. Gini 2013/4
G. Gini 2013/4
Albero n-ario
ogni nodo ha un numero arbitrario di figli
Si usa ad esempio per rappresentare tassonomie e
organizzazioni gerarchiche
definito ricorsivamente:
<albero> = | nodo seguito da
un numero finito di alberi
G. Gini 2013/4
G. Gini 2013/4
In LISP memorizzato
come albero binario
G. Gini 2013/4
Ingestibile
G. Gini 2013/4
A
B
null
Sottoalbero B
B
G. Gini 2013/4
Sottoalbero C
null
/
Padre
Nodo
G. Gini 2013/4
Primo
Figlio Fratello
Attraversamento di albero
Un processo per visitare i nodi di un albero in un
dato ordine detto attraversamento (tree
traversal)
Ogni attraversamento una enumerazione dei
nodi dellalbero
G. Gini 2013/4
G. Gini 2013/4
Esempi visita df
Previsita
Postvisita
Simmetrica
G. Gini 2013/4
e
d
a b c d e f g
void preorder (tree rt)
{
if (rt == NULL)
return; // Empty
visit(rt);
preorder(rt->left);
preorder(rt->right);
}
G. Gini 2013/4
T
a
b
c
e
d
c d b f g e a
void postorder(tree rt)
{ if (rt == NULL) return; // Empty
postorder(rt->left);
postorder(rt->right);
visit(rt);
}
G. Gini 2013/4
G. Gini 2013/4
e
d
c b d a f e g
in ampiezza
T
a
b
c
e
d
Sequenza: a b e c d f g
G. Gini 2013/4
G. Gini 2013/4
void main() {
int i;
do {
printf("Inserire un intero (-1 = fine):");
scanf("%d",&i);
if(i==-1) break;
insertNum(i);
} while(i!=-1);
deepFirstVisit(root);
printf("\n");
breadthFirstVisit(root);
printf("\nInserire il valore da cercare: ");
scanf("%d",&i);
if(search(root,i))
printf("Trovato\n");
else printf("Niente da fare!\n");
}
G. Gini 2013/4
void insertNum(int i) {
if(root==NULL) {
root=(TREE *) malloc(sizeof(TREE));
root->data=i;
root->left=NULL;
root->right=NULL;
} else orderedInsert(root,i);
}
int search(TREE *t, int numero) {
if(t==NULL)
return 0;
if(numero==t->data)
return 1;
if(numero > t->data)
return search(t->right,numero);
else
return search(t->left,numero);
}
G. Gini 2013/4
G. Gini 2013/4
= postorder
= preorder
G. Gini 2013/4
47
25
11
7
17
77
43
31 44
G. Gini 2013/4
65
68
93
Esempi
rappresentare con alberi binari diversi linsieme ordinato
dei numeri dispari da 1 a 11.
7
/ \
3
9
/ \
\
1
5
11
3
/ \
1
7
/ \
5
9
\
5
/ \
3
9
/
/ \
1
7
11
11
G. Gini 2013/4
La
La radice
radice contiene
contiene 66
44 << 66
44 sta
sta nel
nel sottoalbero
sottoalbero
sinistro
sinistro di
di 66
T
66
22
11
88
44
33
G. Gini 2013/4
Ricerca
Ricerca del
del valore
valore 44
44 >> 22
44 sta
sta nel
nel sottoalbero
sottoalbero
destro
destro di
di 22
T
66
22
11
88
44
33
G. Gini 2013/4
Ricerca
Ricerca del
del valore
valore 44
44 == 44
trovato!
trovato!
T
66
22
11
88
44
33
G. Gini 2013/4
G. Gini 2013/4
G. Gini 2013/4
Dizionari
Dizionario
Insieme dinamico che implementa le seguenti
funzionalit
Cerca un item
Inserisci un item
Cancella un item
Come implementarlo?
Array (ordinato)
Lista (non ordinata)
Albero binario di ricerca
G. Gini 2013/4
Definizione
Le chiavi appartengono ad un
insieme totalmente ordinato
Propriet
1.
Le chiavi dei nodi del sottoalbero
sinistro di u sono minori di u.key
2.
Le chiavi dei nodi del sottoalbero
destro di u sono maggiori di u.key
3.
Tutti nodi i del sottoalbero sinistro
di u sono tali che per tutti nodi r del
sottoalbero destro di u, l.key <=
r.key
G. Gini 2013/4
Propriet di ordine
Come visitare l'albero per ottenere la lista ordinata
dei valori? Depth first inorder
implementazione
Ogni nodo deve mantenere
G. Gini 2013/4
Padre
Key, Value
Figlio
Figlio
sinistro destro
Ricerca - chiave
---------------------------------------------------------tree ricerca (item x , tree-item tree)
--------------------------------------------------------if vuoto?(tree) then return NULL
if x= tree.key then return tree
if x < tree.key then return
ricerca (x, left(tree))
if x > tree.key then return
ricerca (x, right (tree))
G. Gini 2013/4
Sottoalbero con
radice 8:
T
- minimo 1
- massimo 4
- minimo 8
- massimo 15
66
22
88
44
11
12
12
99
33
G. Gini 2013/4
15
15
G. Gini 2013/4
in forma iterativa
G. Gini 2013/4
Ricerca successore/predecessore
Definizione
Il successore di un nodo u il
pi piccolo nodo maggiore di u
T
6
u
PRIMO CASO
u ha un figlio destro
Il successore u' il minimo
del sottoalbero destro di u
12
Esempio:
successore di 2 3
u'
G. Gini 2013/4
15
Ricerca successore/predecessore
T
u'
SECONDO CASO
u non ha un figlio destro
Il successore il primo avo
u' tale per cui u sta nel
sottoalbero sinistro di u'
66
22
11
Esempio:
successore di 4 6
88
12
12
33
44
G. Gini 2013/4
99
15
15
Ricerca successore/predecessore
G. Gini 2013/4
h = altezza dellalbero
Inserimento e rimozione
Quando si inserisce o si rimuove un elemento la
struttura dellalbero cambia.
Lalbero modificato deve mantenere le propriet di
un albero binario di ricerca.
Linserimento risulta essere unoperazione immediata.
La rimozione di un elemento pi complicata, proprio
perch bisogna essere certi che lalbero rimanga un
albero binario di ricerca.
Dopo un certo numero di operazioni un albero si pu
sbilanciare
G. Gini 2013/4
Inserimento
5 < 6
Inserimento chiave 5
Dovr essere nel
sottoalbero sinistro
Si inserisce sempre
come foglia
5 > 2
5 > 4
66
22
88
4
4
11
12
12
33
G. Gini 2013/4
55
99
15
15
Inserimento
Inserire z con key = 14
9
5
15
4
2
1
7
6
16
12
8
11
13 y
z 14
G. Gini 2013/4
19
18
21
Inserimento - esempio
Inserire la sequenza <5, 4, 7, 2, 6, 8>
5
4
2
5
7
4
2
7
6
G. Gini 2013/4
4
2
7
6
Inserimento - esempio
5
4
2
7
6
G. Gini 2013/4
Inserimento - esempio
Inserire la sequenza <8, 7, 6, 4, 5, 2>
8
7
6
Linserimento pu portare
ad avere alberi sbilanciati
4
2
La struttura dellalbero
risulta diversa a seconda
della sequenza di
inserimento!
5
G. Gini 2013/4
Inserimento in C
void orderedInsert(TREE *nodo, int numero) {
TREE *nuovo;
if(numero == nodo->data) return;
if(numero > nodo->data) {
if(nodo->right == NULL) {
nuovo=(TREE *) malloc(sizeof(TREE));
nuovo->data=numero;
nuovo->left=NULL;
nuovo->right=NULL;
nodo->right=nuovo;
} else orderedInsert(nodo->right, numero);
} else {
if(nodo->left == NULL) {
nuovo=(TREE *) malloc(sizeof(TREE));
nuovo->data=numero;
nuovo->left=NULL;
nuovo->right=NULL;
nodo->left=nuovo;
} else orderedInsert(nodo->left, numero);
}}
G. Gini 2013/4
inserimento in C
tree orderedInsert(tree nodo, int numero) {
tree *nuovo;/* ritorna il puntatore allalbero*/
if(numero == nodo->data) return nodo;
if(numero > nodo->data) { /* va inserito a destra
if(nodo->right == NULL) {/* non c figlio destro*/
nuovo=malloc(sizeof(tree));
nuovo->data =numero;
nuovo->left =NULL;
nuovo->right =NULL;
nodo->right =nuovo;
return nodo;
} else return orderedInsert(nodo->right, numero);
} else { /* va inserito a sinistra*/
if(nodo->left == NULL) {
nuovo=malloc(sizeof(tree));
nuovo->data=numero;
nuovo->left=NULL;
nuovo->right=NULL;
nodo->left=nuovo;
return nodo;
} else return orderedInsert(nodo->left, numero);
}}
G. Gini 2013/4
Complessit inserimento
Per inserire z si usano due puntatori y e x. Il
puntatore a x scende lalbero , y punta al padre di x.
Nel ciclo while i due puntatori (x e y) scendono
lalbero. x scende al figlio sinistro o destro a seconda
dellesito del confronto di key[z] con key[x]. Ci si ferma
quando x = NIL e x occupa la posizione in cui z verr
inserito.
I passi necessari per linserimento sono non pi del
cammino massimo tra la radice e una foglia (cio
laltezza).
Nel caso ricorsivo, il numero delle chiamate ricorsive
dipende da h.
G. Gini 2013/4
cancellazione
Cancellare z, che ha chiave 14
Caso 1: z senza figli (foglia) si rimuove
9
5
15
4
2
1
7
6
16
12
8
11
z 14
G. Gini 2013/4
19
13
18
21
cancellazione
Caso 2: cancellare z (che ha chiave 16) con 1 figlio si
riattacca il figlio al padre di z
9
15
5
4
2
1
7
6
z 16
12
8
11
19
13
14
G. Gini 2013/4
18
21
cancellazione
Caso 3: cancellare z (che ha chiave 12) con 2 figli.
9
15
5
4
2
1
7
6
16
z 12
8
11
13
Successore di z:
Viene rimosso e
va al posto di z.
G. Gini 2013/4
19
14
18
21
cancellazione
Caso 3: z con 2 figli.
9
5
15
4
2
1
7
6
16
y 13
8
11
19
14
18
z 12
G. Gini 2013/4
21
cancellazione
tre casi:
1. Se z non ha figli, allora si modifica puntatore a z, che
punta non pi a z ma a NIL.
2. Se z ha un unico figlio, allora si taglia fuori z dallalbero,
facendo puntare il puntatore a z allunico figlio di z.
3. Se z ha due figli, allora si individua il successore, ossia il
minimo del suo sottoalbero destro.
1. Il successore y ha nessun figlio o 1 figlio. Quindi y
prende il posto di z, riconducendosi al caso 1 e 2.
Alla fine i dati in y vengono copiati in z.
G. Gini 2013/4
Passi necessari
Loperazione di rimozione pu richiede che
SUCCESSOR(z) venga eseguito.
SUCCESSOR(z) richiede tempo che dipende da h
anche TREE-INSERT() e TREE-DELETE() richiedono
tempo che dipende dallaltezza
G. Gini 2013/4
Cancellazione pseudocodice
TREE-DELETE(T, z)
1 if left (z)= NIL or right(z) = NIL
2
then y := z
// z ha 0 o 1 figlio, copialo in y
3
else y := TREE-SUCCESSOR(z) // z ha 2 figli, trova
succ(z)
4 if left (y) NIL
// y nodo da eliminare
5
then x := left(y)
// x figlio di y
6
else x := right (y)
G. Gini 2013/4
Cancellazione -continua
7 if x NIL then
// se y ha il figlio
8
then p(x) := p(y)
// taglia fuori y e aggiorna il padre
9 if p (y)= NIL
10
then root (T):= x
// se y la radice, x diventa radice
11
else if y = left(p(y)) // collega x al padre di y
12
then left(p(y)) := x
13
else right(p(y)) := x
14 if y z then
// se y il successore
15
key (z):= key(y)
// ricopia i dati di y in z
16 return y
G. Gini 2013/4
Cancellazione correttezza
Caso 1 - nessun figlio
Eliminare foglie non cambia la propriet di ordine dei nodi
rimanenti
G. Gini 2013/4
G. Gini 2013/4
h = altezza dellalbero
bilanciamento
Fattore di bilanciamento
Il fattore di bilanciamento (v) di un nodo v la massima
differenza di altezza fra i sottoalberi di v
In generale:
Sono necessarie tecniche per mantenere bilanciato
l'albero
G. Gini 2013/4
Per approfondire
Stanford Encyclopedia of philosophy
Temi generali (Recursion, computer science, ..)
Autori
G. Gini 2013/4
Esercizio 1
1.
2.
G. Gini 2013/4
Soluzione 1
int sommaNodi(tree t) {
if (t==NULL) return 0;
return t->info+sommaNodi(t->left)+sommaNodi(t->right);}
int max(int a,int b) {
if(a>b) return a;
else return b;}
int max3(int a,int b,int c) {
return max(a,max(b,c));}
int cercaMax(tree t) {
if (t==NULL) return 0;
if (t->left==NULL && t->right==NULL) return t->info;
if(t->left==NULL)
return max(t->info, cercaMax(t->right));
if(t->right==NULL)
return max(t->info, cercaMax(t->left));
return max3(cercaMax(t->right), cercaMax(t->left), t->info);
}
G. Gini 2013/4
Esercizio 2
Un albero binario si dice isobato se tutti i cammini dalla radice alle
foglie hanno la stessa lunghezza
Data la seguente definizione di albero
typedef struct EL { int dato;
struct EL *left;
struct EL *right; } Node;
typedef Node *tree;
codificare una funzione che riceve in input un albero e restituisce 1
se lalbero isobato, 0 altrimenti.
G. Gini 2013/4
Soluzione 2
Uso funzione ausiliaria
int contaProfonditaSeUguale(tree t) {
int s, d;
if(t==null) return 0;
s=contaProfonditaSeUguale(t->left);
d=contaProfonditaSeUguale(t->right);
if(d==-1||s==-1)
return -1
if(d==s)
return d+1;
if(d==0)
return s+1;
if(s==0)
return d+1;
}
return -1;//d!=s
int isobato(tree t) {
if(contaProfonditaSeUguale(t)==-1)
return 0;
else
return 1;
}
G. Gini 2013/4
Esercizio 3
G. Gini 2013/4
Soluzione 3
int contaNodi ( tree t ) {
if ( t == NULL ) return 0;
else return (contaNodi(t->left) + contaNodi(t->right) + 1); }
int artussiano(tree t) {
if(t==NULL) return 1;
if(t->left==NULL && t->right==NULL) return 1;
if(t->left==NULL) return artussiano(t->right)
if(t->right==NULL) return artussiano(t->left)
if(contaNodi(t->left)==contaNodi(t->right) &&
artussiano(t->left) && artussiano(t->right))
return 1;
return 0;
}
G. Gini 2013/4
Esercizio 4
G. Gini 2013/4
Soluzione 4
int maxPunti ( tree t ) {
int D, S;
if (t == NULL) return 0;
S = maxPunti( t->left );
D = maxPunti( t->right );
if ( S > D ) return S + t->dato;
else return D + t->dato;
}
int esisteCammino ( tree t, int k ) {
int D, S;
if (t == NULL && k==0) return 1;
if (t == NULL) return 0;
if (k t->dato <0) return 0;
return (esisteCammino(t->left, k - t->dato) ||
esisteCammino(t->right, k - t->dato));
}
G. Gini 2013/4
Esercizio 5
G. Gini 2013/4
Soluzione 5
void creaLista(tree t, Lista *lis) {
Lista temp;
if(t==NULL) return;
if(t->left==NULL && t->right==NULL) {
temp=*lis;
*lis=(Lista)malloc(sizeof(NodoLista));
*lis->foglia=t;
*lis->next=temp;
}
if(t->left!=NULL)
creaLista(t->left, lis);
if(t->right!=NULL)
creaLista(t->right, lis);
}
G. Gini 2013/4
Esercizio 6
Si definisca una struttura dati adatta a rappresentare un albero Nario. Per semplicit si consideri il caso in cui i nodi contengono,
come dati utili, dei semplici numeri interi.
G. Gini 2013/4
soluzione 6
int contaNodi ( Albero t ) {
if ( t == NULL ) return 0;
else
return 1 + contaRami( t->rami );
}
int contaRami ( Ramo * b ) {
if ( b == NULL )
return 0;
else
return contaNodi( b->child ) + contaRami( b->next );
}
G. Gini 2013/4