350 likes | 471 Views
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:
E N D
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, … , xktale che xi è il genitore di xi+1 lunghezza del cammino: k(numero degli archi) antenato e discendente: dato un nodo x di un albero Tcon 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] head[T]puntatore al primo elemento x della lista (radice) Albero vuoto: head[T] = NIL 1 2 2 NIL 1 NIL NIL 4 NIL 6 NIL NIL 5 NIL NIL
Alberi in C // definizione della struttura nodo typedefstruct elem{ int info; struct elem* left; struct elem* right; } etree; // definizione del puntatore ai nodi dell’albero typedef etree* ptree;
Verificare se un nodo è una foglia isLeaf(x) return x ≠ NIL and left[x] = NIL and right[x] = NIL void isLeaf(ptree x) { return ((x ≠ NULL) && (x->left == NULL) && (x->right == NULL)); }
Notazione parentetica Esiste anche una notazione testuale detta PARENTETICA ( 1 ( 2 ( 4 () () ) ( 5 () () ) ) ( 3 ( 6 () () ) ( 7 () () ) ) ) Un nodo n che non ha figli è rappresentato da ( n () () ) 1 2 3 4 5 6 7
Notazione parentetica Scriviamo in c una funzione per stampare in notazione parentetica un albero binario (senza indentare) 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 (indentando) • come indentare? sul livello i indentiamo con 2i spazi • ogni volta che incontriamo un nodo ci chiediamo • se è una foglia allora stampiamo il valore del nodo senza andare a capo • se non è una foglia allora stampiamo il valore del nodo e andiamo a capo • una volta stampato il valore del nodo richiamiamo la funzione in modo ricorsivo sui sottoalberi sinistro e destro
Notazione parentetica (indetata) void Indenta(int livello) { int i = 0; for (i = 0; i < 2*livello; i++) printf(“ ”); }
Notazione parentetica (indentato) 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); }
Altezza di un albero Massima profondità a cui si trova una foglia ovvero numero di archi nel cammino dalla radice alla foglia più profonda Massimo(a,b) { if a > b then return a else return b } Profondita(T) { if T = NIL then return -1 else s = Profondita(left[T]) d = Profondita(right[T]) return (Massimo(s,d)+1) }
Altezza di un albero int Massimo(int a, int b) { if (a > b) return a; } 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); } }
Presenza di un nodo in un Albero 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) } 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)); }
Albero crescente Un albero è crescente se: a) è vuoto b) 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; }
Visite negli alberi 1 2 3 4 5 6 7 • 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
Visita in preordine 1 2 3 4 5 6 7 PREORDER(T) if T ≠ NIL then visita head[T] PREORDER(left[T]) PREORDER(right[T])
Visita in preordine 1 2 3 4 5 6 7 void VisitaPreordine(ptree t) { if (t == NULL) return; printf(“%d “,t->info); VisitaPreordine(t->left); VisitaPreordine(t->right); }
Visita in postordine 1 2 3 4 5 6 7 POSTORDER(T) if T ≠ NIL then POSTORDER(left[T]) POSTORDER(right[T]) visita head[T]
Visita in preordine 1 2 3 4 5 6 7 void VisitaPostordine(ptree t) { if (t == NULL) return; VisitaPostordine(t->left); VisitaPostordine(t->right); printf(“%d “,t->info); }
Visita Simmetrica 1 2 3 4 5 6 7 SIMMETRICA(T) if T ≠ NIL then SIMMETRICA(left[T]) visita head[T] SIMMETRICA(right[T])
Visita in preordine 1 2 3 4 5 6 7 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”
Cancellazione di un nodo: dato il valore // 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); } } }
Funzione per valutare il numero di nodi con valore val 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 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) l x 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
Ricerca di un nodo in una lista // 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; }
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
Inserimento di un nuovo nodo dopo un nodo dato // 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; } }
Corso di Algoritmi e strutture Dati APPUNTI SUL LINGUAGGIO C Esercizi su alberi FINE