Docente: Giuseppe Scollo
Università di Catania, sede di Comiso (RG)
Facoltà di Scienze Matematiche, Fisiche e Naturali
Corso di Studi in Informatica applicata, AA 2007-8
attraversamento di un albero: visita (l'informazione associata a) tutti i vertici dell'albero
per gli alberi binari, si hanno tre discipline di visita ricorsiva, cioè basate sulla struttura ricorsiva dell'albero:
definizione ricorsiva di funzione di attraversamento in preordine
(btree* = btlink
):
void attraversa(btlink h, void visita(btlink)) { if (h == 0) return; visita(h); attraversa(h->l, visita); attraversa(h->r, visita); }
per ottenere da questa le analoghe definizioni per le altre due discipline
ricorsive di attraversamento basta spostare opportunamente l'invocazione di
visita(h)
le tre discipline di visita ricorsiva di alberi binari danno soluzioni a svariati problemi
le suddette discipline di visita possono essere realizzate anche con algoritmi non ricorsivi, impiegando una pila
push
dei sottoalberi nella pila è l'inverso di quello della loro visita
void attraversa(btlink h, void visita(btlink)) { Stack<btlink> s(max); s.push(h); while (!s.empty()) { visita(h = s.pop()); if (h->r != 0) s.push(h->r); if (h->l != 0) s.push(h->l); } }
nessuna delle tre suddette discipline di visita è adeguata al caso in cui si voglia attraversare un albero binario facendo procedere assieme gli attraversamenti dei suoi sottoalberi
è notevole il fatto che un algoritmo a tal scopo si può ottenere da quello non ricorsivo di attraversamento preordine, semplicemente adoperando una coda in luogo della pila, con le corrispondenti operazioni di inserimento e rimozione, dove l'ordine degli inserimenti coincide stavolta con quello della successiva visita, per via della disciplina FIFO
void attraversa(btlink h, void visita(btlink)) { Queue<btlink> q(max); q.put(h); while (!q.empty()) { visita(h = q.get()); if (h->l != 0) s.put(h->l); if (h->r != 0) s.put(h->r); } }
è frequente la costruzione esplicita di alberi binari in algoritmi ricorsivi divide et impera
riconsideriamo ad es. il problema della ricerca del massimo fra gli elementi di un array di interi, proposto nella lezione 20: vediamone ora una soluzione basata sulla costruzione di un torneo:
l'algoritmo è simile a quello già visto nella lezione 20, eccetto che la soluzione è qui prodotta dalla funzione ricorsiva di costruzione del torneo su (una parte de)gli elementi dell'array, che restituisce un puntatore alla radice del torneo costruito
struct btree { Elem elem; btree *l, *r; btree(Elem x) { elem = x; l = 0; r = 0; } }; typedef btree* btlink; btlink max(Elem a[], int l, int r) { int m = (l+r)/2; btlink x = new btree(a[m]); if (l == r) return x; x->l = max(a, l, m); x->r = max(a, m+1, r); Elem u = x->l->elem, v = x->r->elem; if (u > v) x->elem = u; else x->elem = v; return x; }
un torneo è un caso particolare di una struttura dati molto utile per la realizzazione di efficienti algoritmi di ricerca su insiemi ordinati:
è facile intuire come, ad es., l'algoritmo di ricerca binaria di un elemento in un array ordinato possa tradursi in un algoritmo di ricerca in un BST
concludiamo con dei cenni sulla generalizzazione degli algoritmi (ricorsivi e non) visti per l'attraversamento di alberi binari all'attraversamento di grafi, ad es. rappresentati da liste di adiacenza
si possono distinguere due discipline di attraversamento:
analogamente al caso degli alberi, per i grafi: