Scaricare la presentazione
La presentazione è in caricamento. Aspetta per favore
1
Esercizi su alberi binari
Dato un albero binario, chiamiamo altezza minimale di un nodo v la minima distanza di v da una foglia. Scrivere una funzione che calcola l’altezza minimale della radice di un albero. Scrivere una funzione che determini se un albero binario è completo. Scrivere una funzione ricorsiva che, dato un albero in input, inserisca in una lista solo i nodi di livello pari. Scrivere una funzione che, dato un albero che rappresenta il valore di un espressione (es. (1+2)*5), ne calcoli il valore e lo restituisca in output Scrivere una funzione che, dato un albero binario, inserisca come figli sinistro e destro di ciascuna foglia x dell'albero rispettivamente il minimo e il massimo tra tutti i nodi nel cammino dalla radice alla foglia x NOTA: scrivere sempre pre e post condizione di ogni funzione
2
Esercizio 1 Pre condizioni: Post condizioni:
Dato un albero binario, chiamiamo altezza minimale di un nodo v la minima distanza di v da una foglia. Scrivere una funzione che calcola l’altezza minimale della radice di un albero. Pre condizioni: La funzione prende in ingresso un albero binario Post condizioni: La funzione restituisce un intero che rappresenta l’altezza minimale della radice dell’albero.
3
Implementazione int alt_min(tree *t) { if (t == NULL) return -1;
if (t->sx == NULL) return alt_min(t->dx)+1; if (t->dx == NULL) return alt_min(t->sx)+1; return min(alt_min(t->sx), alt_min(t->dx))+1; }
4
Esercizio 2 Un albero di dice completo se le foglie si trovano tutte allo stesso livello e ogni nodo che non e' una foglia ha esattamente 2 figli, (ovvero se in ogni nodo la profondita' del sottoalbero sinistro coincide con quella del sottoalbero destro). Scrivere una funzione che determini se un albero in input è completo. Pre condizioni: La funzione prende in ingresso un albero binario Post condizioni: La funzione restituisce 1 se l'albero è completo, 0 altrimenti.
5
Implementazione inefficiente
int profondita(tree *t) { if (t == NULL) return 0; return max(profondita(t->sx), profondita(t->dx))+1; } int completo1(tree *t) if (t == NULL) return 1; return profondita(t->sx) == profondita(t->dx) && completo1(t->sx) && completo1(t->dx);
6
Due implementazioni efficienti
Memorizzare la profondità nei nodi dell'albero una volta per tutte e solo dopo chiamare la funzione completo, che – invece di chiamare ricorsivamente la funzione profondità – leggerà la profondità da un campo nel nodo. Utilizzar le due proprietà di un albero completo (un nodo ha 0 o 2 figli; le foglie si trovano tutte allo stesso livello)
7
Implementazione efficiente
int completo2(tree *t, int liv, int *liv_foglie) { if (t == NULL) return 1; /* foglia */ if (t->sx == NULL && t->dx == NULL) /* prima foglia incontrata: memorizzo il livello della foglia da confrontare con quello delle prossime */ if (*liv_foglie == -1) { *liv_foglie = liv; return 1; } else return liv == *liv_foglie; } /* un solo figlio */ else if (t->sx == NULL || t->dx == NULL) return 0; /* due figli */ return completo2(t->sx, liv+1, liv_foglie) &&completo2(t->dx, liv+1, liv_foglie); int completo(tree *t) int liv_foglie = -1; return completo(t, 0, &liv_foglie);
8
Esercizio 3 Scrivere una funzione mutuamente ricorsiva che, dato un albero in input, inserisca in una lista solo i nodi di livello pari. Pre condizioni: La funzione prende in ingresso un albero binario e un puntatore a puntatore a una lista Post condizioni: La funzione imposta i nodi di livello pari nella lista.
9
Implementazione void attraversa_pari(tree *t, list **l);
void attraversa_dispari(tree *t, list **l) { if (t == NULL) return; attraversa_pari(t->sx, l); attraversa_pari(t->dx, l); } void attraversa_pari(tree *t, list **l) list *nuovo; nuovo = (list *)malloc(sizeof(list)); nuovo->next = *l; nuovo->dato = (void *)t->dato; *l = nuovo; attraversa_dispari(t->sx, l); attraversa_dispari(t->dx, l);
10
Esercizio 4 Scrivere una funzione che, dato un albero che rappresenta il valore di un espressione (es. (1+2)*5), ne calcoli il valore e lo restituisca in output. Nota: I nodi intermedi rappresentano gli operatori, le foglie dell'albero rappresentano gli operandi (tenendo conto di queste informazioni è possibile utilizzare la solita struttura tree) Pre condizioni: La funzione prende in ingresso un albero binario che rappresenta un'espressione (si suppone che l'albero codifichi correttamente l'espressione) Post condizioni: La funzione restituisce il valore dell'espressione.
11
Implementazione int get_value(tree *t) { int val_sx, val_dx;
/* foglia: intero */ if (t->sx == NULL && t->dx == NULL) return t->val; /* nodo intermediio: operatore val_sx = get_value(t->sx); val_dx = get_value(t->dx); switch(t->val) case '+': return val_sx+val_dx; case '-': return val_sx-val_dx; case '*': return val_sx*val_dx; case '/': return val_sx*val_dx; } printf(“espressione mal formata!”); return -1; Per esercizio, estendere la struttura tree in modo da includere il tipo di operatore e valori double.
Presentazioni simili
© 2024 SlidePlayer.it Inc.
All rights reserved.