Sei sulla pagina 1di 102

Dipartimento di

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

Famiglia - 1700 circa


G. Gini 2013/4

The art of computer programming


Donald Knuth, 1968-1973
Enciclopedia dellinformatica in 7 volumi
TeX

"Beware of bugs in the above code; I have only proved it correct,


not tried it."

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 *

Osservazione: vale anche per il linguaggio naturale


G. Gini 2013/4

term
fact

fact

* 123 + 15

fact

54

parsing di espressioni

notazione algebrica normale (INORDER)

(9+7)*(3-(4+1))
notazione polacca inversa - postfissa(POSTORDER)

97+341+-*

G. Gini 2013/4

Albero binario def ricorsiva


Un albero vuoto oppure un nodo con due figli, sinistro e destro,
che sono a loro volta alberi binari.
<albero-binario> ::= NIL |
<nodo> <albero-binario><albero-binario>
Rappresentazione grafica: ogni nodo dell'albero contiene un
elemento dell'insieme e due frecce, a figlio sinistro e a figlio
destro.

D
C

G. Gini 2013/4

primitive sugli alberi binari

1. vuoto? (cio NULL)


2. radice
3. sinistro
4. destro
5. aggiungi-nodo
6. rimuovi-nodo
Notazioni
Nodo, arco
Figlio, discendente, genitore,..
Radice, foglia, ramo
G. Gini 2013/4

Es: contare le foglie


contafoglie(x) - per x vuoto zero, altrimenti la somma delle
foglie del sottoalbero sinistro e del sottoalbero destro.
-------------------------------------int contafoglie ( tree-item x)
--------------------------------------if vuoto? (x) then return 0
if foglia(x) then return 1
else return (contafoglie (sinistro(x)) +
(contafoglie (destro(x))
Funzione ausiliaria
-------------------------------------boolean foglia ( tree-item x)
--------------------------------------if vuoto? (x) then return false
if vuoto?(sinistro(x)) AND vuoto?(destro(x))
then return true
G. Gini 2013/4

Es: conta foglie (in C)


int conta-f ( tree t ) {
if ( t == NULL ) /*se vuoto*/
return 0;
if ( t->left == NULL && t->right == NULL); /*se foglia*/
return 1;
return conta-f( t->right ) + conta-f(t->left);
}

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

Albero binario completo (Complete binary tree) - se


laltezza dellalbero d, allora tutti i nodi tranne
eventualmente quelli al livello d sono full.

G. Gini 2013/4

Full Binary Tree


TEOREMA: il numero di foglie in un full binary tree non vuoto il
numero dei nodi interni +1

Dimostrazione (per induzione matematica):

Caso base: un full binary tree con 1 nodo interno ha due foglie

Ipotesi induttiva: assumiamo che un full binary tree T con n-1


nodi interni ha n foglie
Passo induttivo: dato lalbero full binary tree T con n nodi interni,
considera il nodo interno I con due figli foglia.
Togli i figli di I e ottieni lalbero T.

Per ipotesi induttiva, T un full binary tree con n foglie.


Riaggiungi i figli di I. Il numero dei nodi interni ora aumenta

di 1 e diventa n. Il numero di foglie pure esso aumenta di 1.

G. Gini 2013/4

Rappresentazioni per alberi


Anche per gli alberi come per le liste si possono avere 2
rappresentazioni:
1. mediante array
2. a puntatori
Per rappresentare un albero binario di profondit n
basta un array in cui riservare memoria per ogni
nodo; nel caso di alberi sbilanciati i nodi mancanti
avranno un valore di default (-1).
7
/\
3 9
/\ \
1 5 11

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.

typedef struct Nodo {


Tipo data;
struct Nodo *left;
struct Nodo *right;
} nodo;

typedef nodo * tree;


C

G. Gini 2013/4

nota

Le foglie sono implementate come i nodi interni,


quindi si spreca spazio per i puntatori nulli.

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

--------------------------------tree trova ( tree t, d)


-------------------------------if null(t) then return NULL
if ( t->dato = d ) then return t; /*trovato*/
return (trova( t->left, d) || trova( t->right, d ))

G. Gini 2013/4

ricerca con assegnamenti (in C)


tree trova ( tree t, int d) {
tree temp;
if ( t == NULL) return NULL;
if ( t->dato == d ) return t;
temp = trova( t->left, d );
if ( temp == NULL )
return trova( t->right, d );
else
return temp;
}

G. Gini 2013/4

Es: calcolo della altezza

int height ( tree t ) {


if (t == NULL) return 0;
return max ( heigth( t->left), heigth( t>right ))
+ 1;
}
int max ( int a, int b ) {
if (a >b) return a;
else return b; }
G. Gini 2013/4

Es: conteggio dei nodi - in C


int contaNodi ( tree t )
{
if ( t == NULL )
return 0;
else
return (contaNodi(t->left)
+ contaNodi(t->right)
+ 1); /* c anche il nodo corrente */
}

G. Gini 2013/4

Es: conta i nodi non-foglia


int interni ( tree t ) {
if ( t == NULL ) return 0;
if ( t->left == NULL && t->right == NULL)) return 0;
return 1 + interni( t->right ) + interni(t->left);
}
oppure
int interni2 ( tree t ) {
return contaNodi( t ) conta-f( t );
}
Si noti che la seconda versione scandisce due volte l'intero albero,
impiegando circa il doppio del tempo (e lo stesso spazio in
memoria)
G. Gini 2013/4

Es: numero di nodi su livelli pari


Consideriamo che il livello della radice, cio 0, sia pari .
Un albero col solo nodo radice ha 1 livello.
int evenCount ( tree t ) {
if ( t == NULL ) return 0;
if ( t->left == NULL ) && (t->right==NULL) return 1;
return evenCount( t->left->left) +
evenCount( t->left->right ) +
evenCount( t->right->left ) +
evenCount( t->right->right ) +
1;
}

G. Gini 2013/4

oppure con assegnamenti


int evenCount ( tree t ) {
int n = 1; /* Il nodo corrente sempre su livello pari, per
ipotesi induttiva*/
if ( t == NULL ) return 0;
if ( t->left != NULL ) /* ci sono nodi a sinistra*/
n += evenCount( t->left->left ) + evenCount( t->left>right );
if ( t->right != NULL ) */ ci sono nodi a destra*/
n += evenCount( t->right->left ) + evenCount( t->right>right );
return n;
}
G. Gini 2013/4

Es: numero di nodi su livelli dispari


int oddCount ( tree t, int level ) {
int n;
if ( t == NULL ) return 0;
n = oddCount( t->left, level+1 ) + oddCount( t->right,
level+1 );
if ( level%2 == 1 )
/* == 0 per evenCount */
n++;
return n;
}
oppure

oddCount (t) = contanodi (t) evenCount(t)


G. Gini 2013/4

numero di nodi su livelli dispari


Si pu vedere come il calcolo del numero dei nodi di
livello pari partendo dal livello 1 invece che 0
int oddCount ( tree t) {
if ( t == NULL ) return 0; /* vuoto*/
return evenCount ( t->left ) + evenCount (t->right)
}

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

Conversione in albero binario

Il figlio di sinistra diventa sottoalbero sinistro;


il sottoalbero destro contiene il primo fratello verso
destra

G. Gini 2013/4

Implementazioni sequenziali ad hoc


Lista semplice con i nodi nellordine di visita (ad
esempio pre-order)

Risparmio di spazio ma solo accesso sequenziale


Occorre conservare la struttura dellalbero per poterlo
ricostruire
esempi:
se lalbero full, marca i nodi come foglie o come
interni
AB/DCEG/FHI
usa un simbolo per marcare i NULL
AB/D//CEG///FH//I//
G. Gini 2013/4

Rappresentare solo foglie e struttura

le liste in LISP sono alberi con linformazione (i simboli atomici)


memorizzata nelle foglie.
la lista (a (b c) d (e f g)) corrisponde allalbero seguente

In LISP memorizzato
come albero binario

G. Gini 2013/4

con diverso numero di puntatori

Ingestibile

G. Gini 2013/4

Rappresentazione con 2 puntatori

A
B

Il nodo una struttura con 3


campi:
1. informazione,
2. puntatore al primo figlio,
C
3. puntatore al fratello a
destra
H

null

Sottoalbero B

B
G. Gini 2013/4

Sottoalbero C

null

Realizzazione con 3 puntatori


/
/

/
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

Algoritmi di visita degli alberi


per visitare tutti i nodi di un albero
In profondit (depth-first search, a scandaglio,
DFS)
Vengono visitati i rami, uno dopo laltro
Tre varianti in, pre, post-ordine
In ampiezza (breadth-first search, a ventaglio,
BFS)
A livelli, partendo dalla radice

G. Gini 2013/4

Modi di visita albero binario


Pre-ordine, o pre-visita, o anticipato: visita ogni
nodo prima di visitare i suoi figli
Post-ordine, o post-visita, o posticipato: visita
ogni nodo dopo aver visitato i suoi figli
In-ordine, o simmetrico: visita il sottoalbero di
sinistra, quindi il nodo, poi il sottoalbero di destra

Si estende per albero n-ario


G. Gini 2013/4

Esempi visita df

Previsita
Postvisita
Simmetrica
G. Gini 2013/4

df - ordine anticipato - preorder


T
a
b
c

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

df - ordine posticipato - postorder

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

df - ordine simmetrico - inorder


T
a
b
c
void inorder(tree rt)
{
if (rt == NULL)
return; // Empty
inorder(rt->left);
visit(rt);
inorder(rt->right);
}

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

Albero binario programma in C


typedef struct Tree {
int data;
struct Tree *left;
struct Tree *right;
} TREE;
void insertNum(int i);
void orderedInsert(TREE *, int);
void deepFirstVisit(TREE *t);
void breadthFirstVisit(TREE *t);
int search(TREE *t, int i) ;
TREE *root;

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

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

void deepFirstVisit(TREE *t) {


if(t==NULL) return;
deepFirstVisit(t->left);
deepFirstVisit(t->right);
printf("%d - ",t->data);
}
void breadthFirstVisit(TREE *t) {
if(t==NULL) return;
printf("%d - ",t->data);
breadthFirstVisit(t->left);
breadthFirstVisit(t->right);
}

G. Gini 2013/4

= postorder

= preorder

Alberi adatti per applicazioni di


data base, di dizionari, etc

G. Gini 2013/4

Alberi binari per rappresentare un insieme


ordinato
I valori nel sottoalbero di sinistra sono minori di quello
del padre
I valori nel sottoalbero di destra sono maggiori di
quello del padre

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

Ricerca in albero binario ordinato


Ricerca
Ricerca del
del valore
valore 44

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

Pseudocodice per ricerca


--------------------------------------------------------boolean element-of?(item x , tree-item tree)
--------------------------------------------------------if vuoto?(tree) then return false
if x = radice(tree) then return true
if x < radice(tree) then return
element-of(x, sinistro(tree))
if x > radice(tree) then return
element-of(x, destro(tree))

G. Gini 2013/4

Ricerca in C come predicato


typedef struct Nodo {
Tipo data;
struct Nodo *left;
struct Nodo *right;
} nodo;
typedef nodo * tree;

int searchItem ( Tipo item , tree node) {


if ( node == NULL) return 0;
If (item == node -> data ) return 1;
if ( item < node -> data )
return searchItem ( item , node -> left );
else return searchItem ( item , node -> right );
}
G. Gini 2013/4

Vantaggi alberi binari ordinati


Per vedere se un elemento x contenuto basta
confrontare x con radice: se x minore basta cercare
nel sottoalbero sinistro, se maggiore nel destro.
Quanto vantaggioso?
Se l'albero bilanciato i sottoalberi conterranno la met
degli elementi, quindi ad ogni passo dimezziamo il
numero dei nodi da esaminare.
Il numero dei passi crescer quindi come log2 n.

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

Alberi binari di ricerca (ABR)

Definizione

Ogni nodo u contiene una chiave


u.key associata ad un valore
u.value

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

Alberi binari di ricerca (ABR)


Propriet di ricerca
Le propriet 1. e 2. permettono di realizzare un
algoritmo di ricerca dicotomica

Propriet di ordine
Come visitare l'albero per ottenere la lista ordinata
dei valori? Depth first inorder

implementazione
Ogni nodo deve mantenere

Figlio sinistro, destro


Padre
Chiave
Valore

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

Ricerca del minimo e del massimo


Sottoalbero con
radice 2:

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

Ricerca minimo e massimo


Lelemento minimo quello pi a sinistra
---------------------------------------item MINIMUM (tree-item x)
--------------------------------------if x.left = NULL then return x
return MINIMUM (x.left)
Lelemento massimo quello pi a destra
----------------------------------------item MAXIMUM (tree-item x)
----------------------------------------if x.right = NULL then return x
return MAXIMUM (x.right)

G. Gini 2013/4

in forma iterativa

Montresor usa notazione con .

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

Ricerca: costo computazionale


Le operazioni di ricerca
sono
confinate ai nodi
posizionati
lungo un singolo percorso
dalla radice ad una foglia
Tempo di ricerca:
dipende dallaltezza
caso pessimo?

L'albero una sequenza


lineare n passi
caso ottimo?
albero completo, o
bilanciato. L'altezza
dell'albero con n nodi
log n
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

Inserire la sequenza <5, 4, 7, 2, 6, 8>

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

Inserimento (Cormen) con notazione funzionale


scendere nell'albero fino a che non si raggiunge il posto in cui il nuovo
elemento deve essere inserito, ed aggiungerlo come foglia
TREE-INSERT(T, z)
1 y := NIL
//padre
2 x := root(T)
//figlio
3 while x NIL
//cerca dove inserire
4
do y := x
//memorizza il padre
5
if key (z)< key(x)
6
then x := left (x)
//scende a sinistra
7
else x := right(x)
//scende a destra secondo il confronto
8 z.p := y
// inserisci z come figlio di y
9 if y = NIL
10
then root(T) := z
//l'albero T e' vuoto e z diventa la radice
11
else if key(z) < key(y)
12
then left(y) := z
13
else right(y) := z
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

Caso 2 - solo un figlio (destro o sinistro)


Se u il figlio destro (sinistro) di p, tutti i i valori nel
sottoalbero destro sono maggiori (minori) di p
Quindi f pu essere attaccato come figlio destro (sinistro)
di p al posto di u

Caso 3 - due figli


Il successore s

sicuramente maggiore di tutti i nodi del sottoalbero sinistro di u


sicuramente minore di tutti i nodi del sottoalbero destro di u

Quindi pu essere sostituito a u

G. Gini 2013/4

Modifica: costo computazionale


Le operazioni di modifica
sono confinate ai nodi
posizionati
lungo un singolo percorso
dalla radice ad una foglia
In tempo di ricerca
dipende dallaltezza
dellalbero
Caso pessimo: l'albero
organizzato come una
sequenza lineare - n
Caso ottimo: l'albero
completo, o bilanciato. In
questo caso, l'altezza
dell'albero log n

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

Esempio: albero perfetto:


(v)=0 per ogni nodo v

In generale:
Sono necessarie tecniche per mantenere bilanciato
l'albero

Vedremo alberi rosso-neri

G. Gini 2013/4

Per approfondire
Stanford Encyclopedia of philosophy
Temi generali (Recursion, computer science, ..)
Autori

G. Gini 2013/4

Esercizio 1

1.
2.

Sia data la seguente struttura per la memorizzazione di alberi binari


etichettati con numeri interi:
typedef struct nodo {
int info;
struct nodo *left, *right;
} NODO;
typedef NODO *tree;
scrivere due funzioni ricorsive
int sommaNodi(tree t), che somma i valori delle etichette
nell'albero
int cercaMax(tree t), che cerca il valore dell'etichetta massima
dell'albero.

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

Si consideri la seguente definizione di un albero binario


typedef struct EL { int dato;
struct EL * left, right; } node;
typedef node * tree;
Definiamo un albero come "artussiano" se composto solo da
nodi foglia
nodi con un solo figlio
nodi con due figli aventi lo stesso numero di discendenti
Codificare una funzione che riceve in input un albero e restituisce 1
se lalbero "artussiano", 0 altrimenti. Nel risolvere il problema
consigliato servirsi di opportune funzioni ausiliarie.

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

Si consideri un albero binario in cui il valore dei nodi sempre positivo.

Supponiamo che percorrendo un cammino dalla radice alle foglie si totalizzi


un punteggio pari alla somma dei valori contenuti nei nodi percorsi.

- Scrivere una funzione maxPunti che calcola il punteggio massimo che


possiamo totalizzare percorrendo un cammino dalla radice alle foglie.

-Vogliamo percorrere lalbero dalla radice ad una foglia totalizzando


esattamente un certo punteggio. Scrivere una funzione esisteCammino che
dati un albero ed un intero k, dice se esiste un cammino dalla radice ad una
foglia che permette di totalizzare esattamente k punti.

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

Si consideri la seguente definizione di un albero binario


typedef struct EL { int dato;
struct EL * left, right; } node;
typedef node * tree;
Scrivere una funzione che riceve il puntatore alla radice di un albero e lo
scandisce interamente costruendo una lista tale che abbia tanti nodi quante
sono le foglie dellalbero e che ogni nodo della lista punti ad una diversa
foglia dellalbero. La funzione deve restituire al chiamante il puntatore
allinizio della lista creata.
typedef struct ELLista { tree foglia;
struct ELLista * next; } nodeLista;
typedef nodeLista * Lista;
Si noti che esistono diversi modi di visitare lalbero che originano diverse
liste come risultato.

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.

Ogni nodo contiene, invece di una coppia di puntatori a nodi, come


nel caso degli alberi binari, una lista di puntatori a nodo. Tale lista
una lista concatenata semplice, realizzata tramite la struttura Ramo.
typedef Nodo * Albero;
typedef struct Branch { Albero child; struct Branch * next; }
Ramo;
typedef struct Knot { int dato; struct Branch * rami; } Nodo;

Si progetti la funzione int conta( ) che conta il numero di nodi di


un albero N-ario.

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