corso di algoritmi e strutture dati
DESCRIPTION
Corso di Algoritmi e Strutture Dati. APPUNTI SUL LINGUAGGIO C Esercizi su Alberi. Alberi. Un albero è un insieme di oggetti, chiamati nodi , su cui è definita una relazione binaria G(x,y) – che leggiamo “ x è genitore di y ” – tale che: - PowerPoint PPT PresentationTRANSCRIPT
Corso di Algoritmi e Strutture Dati
APPUNTI SUL LINGUAGGIO C
Esercizi su Alberi
Alberi
Un albero è un insieme di oggetti, chiamati nodi, su cui è definita una relazione binaria G(x,y) – che leggiamo
“x è genitore di y” – tale che:
1. esiste un unico nodo, chiamato radice, che non ha genitori;
2. ogni nodo x diverso dalla radice ha uno ed unico genitore;
3. per ogni nodo x diverso dalla radice esiste un cammino dalla radice a x
• Se x è il genitore di y, allora y è un figlio di x.
• Un albero binario è un albero in cui ogni nodo ha al massimo due figli.
Terminologia
cammino: sequenza di nodi x1, … , xk tale che xi è il genitore di xi+1
lunghezza del cammino: k (numero degli archi)
antenato e discendente: dato un nodo x di un albero T con radice r, qualunque nodo y sul cammino (unico) da r a x è un antenato di x, mentre x è un discendente di y
fratelli: nodi che hanno lo stesso genitore
sottoalbero: insieme costituito da un nodo x e tutti i suoi discendenti; x è la radice del sottoalbero
foglia: nodo senza figli
nodo interno: nodo con uno o piu’ figli
grado: il numero di figli di un nodo x è il grado di x;
Profondita’ o Livello :• La radice ha profondita’ 0
• Se un nodo ha profondita’ k, i suoi figli hanno profondita’ k+1
Altezza dell’albero : massima profondita’ a cui si trova una foglia ovvero numero di archi nel cammino dalla radice alla foglia piu’ profonda
Alberi binari come lista concatenata
Un elemento x di una lista è un oggetto con tre campi
info[x] campo chiave
left[x] puntatore al sottoalbero (figlio) sinistro
right[x] puntatore al sottoalbero (figlio) destro
Chiamiamo tale lista T
Alberi binari come lista concatenata
head[T] puntatore al primo elemento x della lista (radice)
Albero vuoto: head[T] = NIL
head[T]
1
2 NIL2
NILNIL1 NIL4
NILNIL5
NILNIL6
// definizione della struttura nodo
typedef struct elem{
int info;
struct elem* left;
struct elem* right;
} etree;
// definizione del puntatore ai nodi dell’albero
typedef etree* ptree;
Alberi in C
void isLeaf(ptree x) {
return ((x ≠ NULL) && (x->left == NULL) &&
(x->right == NULL));
}
Verificare se un nodo è una foglia
isLeaf(x)
return x ≠ NIL and left[x] = NIL and right[x] = NIL
Esiste anche una notazione testuale detta PARENTETICA
( 1
( 2
( 4 () () )
( 5 () () ) )
( 3
( 6 () () )
( 7 () () ) )
)
Un nodo n che non ha figli è rappresentato da
( n () () )
Notazione parentetica
1
2 3
4 5 6 7
void StampaAlberoNonIndentato(ptree t) {
if (t == NULL) {
printf("()");
return;
}
printf(“( %d ", t->info);
StampaAlbero(t->left);
StampaAlbero(t->right);
printf(" )");
}
Notazione parentetica
Scriviamo in c una funzione per stampare in notazione parentetica un albero binario (senza indentare)
Notazione parentetica
Scriviamo in c una funzione per stampare in notazione parentetica un albero binario (indentando)
1. come indentare? sul livello i indentiamo con 2i spazi
2. ogni volta che incontriamo un nodo ci chiediamo
A. se è una foglia allora stampiamo il valore del nodo senza andare a capo
B. se non è una foglia allora stampiamo il valore del nodo e andiamo a capo
3. una volta stampato il valore del nodo richiamiamo la funzione in modo ricorsivo sui sottoalberi sinistro e destro
void Indenta(int livello) {
int i = 0;
for (i = 0; i < 2*livello; i++) printf(“ ”);
}
Notazione parentetica (indetata)
void StampaLivello(ptree t, int livello) {
if (t == NULL) {
Indenta(livello);
printf("() ");
return;
}
Indenta(livello);
printf("( ");
switch(isLeaf(t)) {
case 0: printf("%d\n", t->info); break;
case 1: printf("%d", t->info); break;
}
StampaLivello(t->left, livello + 1);
StampaLivello(t->right, livello + 1);
Indenta(livello);
printf(")\n");
}
Notazione parentetica (indentato)
void StampaAlberoIndentato(ptree t) {
StampaLivello(t,0);
}
Notazione parentetica (indentato)
Altezza di un albero
Massima profondità a cui si trova una foglia ovvero numero di
archi nel cammino dalla radice alla foglia più profonda
Profondita(T) {
if T = NIL then return -1
else s = Profondita(left[T])
d = Profondita(right[T])
return (Massimo(s,d)+1)
}
Massimo(a,b) {
if a > b then return a
else return b
}
Altezza di un albero
int Profondita(ptree t) {
int s, d;
if (t == NULL)
return -1;
else {
s = Profondita(t->left);
d = Profondita(t->right);
return (Massimo(s, d) + 1);
}
}
int Massimo(int a, int b) {
if (a > b) return a;
}
Presenza di un nodo in un Albero
int Presente(ptree t, int elem){
if (T == NULL) return 0;
else if (t->info == elem) return 1;
else return (Presente(t->left,elem) ||
Presente(t->right, elem));
}
Presente(T, elem){
if (T = NULL) then return false
else if info[T] = elem then return true
else return Presente(left[T],elem) or
Presente(right[T], elem)
}
Albero crescente
Un albero è crescente se:a) è vuotob) esiste un cammino radice-foglia con valori sempre crescenti
Crescente(T) {
if T = NIL then return true
if left[T] = NIL and right[T]= NIL then return true
if left[T] ≠ NIL then
if info[T] < info[left[T]] and
Crescente(left[T]) then return true
if right[T] ≠ NIL then
if info[T] < info[right[T]] and
Crescente(right[T]) then return true
return 0
}
Albero crescente
int Crescente(ptree t) {
if (t == NULL) return 1;
if (t->left == NULL && t->right == NULL) return 1;
if (t->left != NULL) {
if ((t->info < t->left->info) &&
Crescente(t->left)) return 1;
}
if (t->right != NULL) {
if ((t->info < t->right->info) &&
Crescente(t->right)) return 1;
}
return 0;
}
•Visita in preordine: Si analizza la radice, poi si visita sottoalbero sinistro e
sottoalbero destro (Es. 1, 2, 4, 5, 3, 6, 7)•Visita in postordine: Si visita prima sottoalbero sinistro, poi destro, e alla fine
la radice (Es. 4, 5, 2, 6, 7, 3, 1)•Visita Simmetrica: Prima visito il figlio sinistro, poi la radice e quindi il figlio
destro (Es. 4, 2, 5, 1, 6, 3, 7)
•Per tutte le visite: nel caso in cui l’albero sia vuoto non viene eseguita nessuna
azione
1
2 3
4 5 6 7
Visite negli alberi
1
2 3
4 5 6 7
Visita in preordine
PREORDER(T)
if T ≠ NIL then visita head[T]
PREORDER(left[T])
PREORDER(right[T])
1
2 3
4 5 6 7
Visita in preordine
void VisitaPreordine(ptree t) {
if (t == NULL) return;
printf(“%d “,t->info);
VisitaPreordine(t->left);
VisitaPreordine(t->right);
}
1
2 3
4 5 6 7
Visita in postordine
POSTORDER(T)
if T ≠ NIL then POSTORDER(left[T])
POSTORDER(right[T])
visita head[T]
1
2 3
4 5 6 7
Visita in preordine
void VisitaPostordine(ptree t) {
if (t == NULL) return;
VisitaPostordine(t->left);
VisitaPostordine(t->right);
printf(“%d “,t->info);
}
1
2 3
4 5 6 7
Visita Simmetrica
SIMMETRICA(T)
if T ≠ NIL then SIMMETRICA(left[T])
visita head[T]
SIMMETRICA(right[T])
1
2 3
4 5 6 7
Visita in preordine
void VisitaSimmetrica(ptree t) {
if (t == NULL) return;
VisitaSimmetrica(t->left);
printf(“%d “,t->info);
VisitaSimmetrica(t->right);
}
Esercizio
Data una lista L i cui nodi contengono valori interi, scrivere le seguenti procedure (pseudo-codice e implementazione in C):
– CANC-Val(L,x) che cancella nella lista L il nodo con valore x
– CONT-Val(L,x) che conta in L quanti nodi hanno valore x
– INS-Val(L,x,y) che inserisce in L un nodo con valore x dopo il nodo esistente in L con valore y (se il nodo con valore y non esiste allora sarà fatto un inserimento in coda)
Cancellazione di un nodo: dato il valore
CANC-Val(L,val)
if L = NIL then error “la lista è vuota”
else
if info(head(L)) = val then CANC-Testa(L)
else
prev <- head(L)
cur <- next(prev)
while info(cur) ≠ val and cur ≠ NIL do
prev <- cur
cur <- next(cur)
if cur ≠ NIL then next(prev) <- next(cur)
else error “non esiste l’elemento”
// funzione per cancellare in una lista un nodo con un valore dato
void cancellaVal(plist* l, int val){
plist prev,cur;
if (*l==NULL) printf(“La Lista è vuota\n");
else
if (*l->info == val) cancellaInTesta(l);
else {
prev = *l;
cur=prev->next;
while ((cur->info != val) && (cur != NULL)) {
prev = cur;
cur = cur->next;
}
if (cur == NULL) printf(“Non esiste l’elemento\n");
else {
prev->next=cur->next;
free(cur);
}
}
}
Cancellazione di un nodo: dato il valore
CONT-Val(L,val)
cont <- 0
x <- head(L)
while x ≠ NIL do
if info(x) = val then cont++
x <- next(x)
return cont
Funzione per valutare il numero di nodi con valore val
Funzione per valutare il numero di nodi con valore val
int ContaVal(plist l, int val){
int cont = 0;
while (l!=NULL) {
if (l->info == val) cont++;
l = l->next;
}
return cont;
}
Ricerca di un nodo in una lista
void funzione(plist l, valore x)
x
l
SEARCH(L,k)
if NULL(L) then return NIL
else x <- head(L)
while x ≠ NIL and info(x) ≠ k
do x <- NEXT(x)
return x
// funzione per cercare in una lista un nodo
plist search(plist l, int n){
int test = 0;
while ((l!=NULL)&&(!test))
if (l->info == n) test = 1;
else l = l->next;
return l;
}
Ricerca di un nodo in una lista
Inserimento di un nuovo nodo dopo un nodo dato
INS-Val(L,x,y)
k <- SEARCH(L,y)
if k = NIL INS-Coda(L,x)
else
info(z) <- x
next(z) <- next(k)
next(k) <- z
// funzione per inserire un nodo con valore x
void inserisciVal(plist *l, int x, int y) {
plist k = Search(*l,y);
if (k == NULL) inserisciInCoda(l,x);
else {
plist z = (plist)malloc(sizeof(elist));
z->info = x;
z->next = k->next;
k->next = z;
}
}
Inserimento di un nuovo nodo dopo un nodo dato
Corso di Algoritmi e strutture Dati
APPUNTI SUL LINGUAGGIO C
Esercizi su alberi
FINE