Visite di alberi
In questa pagina 6
Una visita accede a tutti i nodi di un albero eseguendo una operazione su ciascuno. Preorder e postorder sono schemi generali: l'operazione di "visita" si sostituisce con quella che serve al problema. Se l'albero è ordinato i figli si toccano nell'ordine dato.
Algoritmo preorder(T, v) Algoritmo postorder(T, v)
Input: nodo v di T Input: nodo v di T
visita v foreach w in T.children(v) do
foreach w in T.children(v) do postorder(T, w)
preorder(T, w) visita vChiamata iniziale: preorder(T, T.root()). Preorder: prima il padre, poi i sottoalberi dei figli. Postorder: prima i sottoalberi dei figli, poi il padre.
Complessità
L'albero della ricorsione (vedi Algoritmi ricorsiviAlgoritmi ricorsivi come induzione eseguita; albero della ricorsione e record di attivazione nello stack; esempi ReverseArray, LinearSum, Power in tempo logaritmico, Fibonacci ricorsivo (esponenziale) e con memoizzazione; analisi della complessità con l'albero della ricorsione e correttezza per induzione.Algoritmi ricorsivi →) ha un nodo per ogni nodo di . Sia il costo della visita di e il numero di figli. La chiamata su costa (il ciclo sui figli, escluse le chiamate ricorsive, più la visita). Poiché (vedi AlberiAlbero radicato (definizione per padre e ricorsiva), terminologia (antenati, discendenti, nodi interni ed esterni, sottoalbero, albero ordinato), profondità, livello, altezza; altezza = massima profondità delle foglie; algoritmi depth e height con costo; somma dei figli = n-1; esempio di algoritmo Omega(n^2) (heightBad).Alberi →):
Se ogni visita costa si ha ; se per ogni si ha .
Esempi
Preorder: indice di un libro. Nodi = capitoli e sezioni, "visita" = stampa del titolo; il preorder produce l'indice nell'ordine di lettura (Capitolo 1, Sezione 1.1, Sezione 1.2, Sezione 1.2.1, ...). È anche il modo in cui i file system mostrano cartelle e file.
Postorder: spazio occupato. Ogni nodo ha size proprio (una directory non include i file contenuti). Si vuole in size lo spazio complessivo. Il padre ha bisogno dei totali dei figli già calcolati, quindi serve il postorder.
Algoritmo diskSpace(T, v)
Input: nodo v di T Output: spazio totale del sottoalbero T_v (e aggiornamento di v.size)
foreach w in T.children(v) do v.size <- v.size + diskSpace(T, w)
return v.sizeEsempio: /(1K) ha docs(1K; file di 3K e 10K) e src(1K; file main.c di 5K e cartella lib(1K) con util.c di 4K). Postorder: a.pdf, b.pdf, docs , main.c, util.c, lib , src , / K. Costo .
L'algoritmo height di AlberiAlbero radicato (definizione per padre e ricorsiva), terminologia (antenati, discendenti, nodi interni ed esterni, sottoalbero, albero ordinato), profondità, livello, altezza; altezza = massima profondità delle foglie; algoritmi depth e height con costo; somma dei figli = n-1; esempio di algoritmo Omega(n^2) (heightBad).Alberi → è un altro postorder: la "visita" calcola l'altezza del nodo dalle altezze dei figli.
Memorizzare profondità e altezza di ogni nodo
- Profondità
v.depth: serve prima il padre, quindi un preorder con la profondità passata come parametro:visita(T, v, d):v.depth <- d, poi per ogni figliovisita(T, w, d+1); chiamata iniziale con . Costo . Invocaredepthda ogni nodo costerebbe invece , fino a . - Altezza
v.height: postorder:v.height <- 0; per ogni figlio dopo la chiamata ricorsivav.height <- max(v.height, 1 + w.height). Costo .
Antenato comune più basso (LCA)
Dati in , è l'antenato comune più profondo. Algoritmo: si calcolano e risalendo; si porta il nodo più profondo a salire di passi, poi si salgono insieme e finché coincidono. Costo , al caso pessimo e senza strutture di appoggio.
Preorder e postorder insieme
Dato l'ordine di visita in preorder, non tutte le sequenze sono postorder possibili. Se in preorder è prima di e in postorder è dopo , allora è antenato di ; se precede in entrambi, sta a sinistra di (non è suo antenato). La radice è sempre la prima in preorder e l'ultima in postorder: lo si usa per escludere sequenze, come nell'Esercizio 6 · preorder e postorder compatibili.
Errori comuni
- Usare il preorder quando il padre ha bisogno dei risultati dei figli (somma, altezza): serve il postorder.
- Scrivere il costo come con numero massimo di figli: è perché .
- Calcolare
depthda ogni nodo (quadratico) invece di passare la profondità nel preorder.
Versione ripasso
- Preorder: visita , poi ricorsivamente i figli. Postorder: prima i figli, poi . Sono schemi generali da adattare (vedi AlberiAlbero radicato (definizione per padre e ricorsiva), terminologia (antenati, discendenti, nodi interni ed esterni, sottoalbero, albero ordinato), profondità, livello, altezza; altezza = massima profondità delle foglie; algoritmi depth e height con costo; somma dei figli = n-1; esempio di algoritmo Omega(n^2) (heightBad).Alberi →).
- Costo: , con costo della visita di , perché la chiamata su costa e . Con : .
- Preorder: indice di un libro;
v.depthpassando come parametro ( ai figli), . - Postorder:
diskSpace(somma dei figli nel padre),heightev.height. Esempio:/K. - LCA: si livellano le profondità (salendo del dislivello) e poi si sale insieme finché coincidono; .
- Compatibilità preorder/postorder: radice prima in preorder e ultima in postorder; prima di in preorder e dopo in postorder antenato di ; prima in entrambi a sinistra di .
- Esempio disk space:
/(1) condocs(1; file 3 e 10) esrc(1;main.c5 elib(1) conutil.c4):lib,src,docs,/K. - Profondità in preorder:
visita(T,v,d):v.depth <- devisita(T,w,d+1)per ogni figlio; . - Errori: preorder dove serve il postorder; costo ;
depthda ogni nodo.