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 vDefinizione (visite in preorder e postorder). Chiamata iniziale:
preorder(T, T.root()). Preorder: prima il padre, poi i sottoalberi dei figli. Postorder: prima i sottoalberi dei figli, poi il padre.
Esempio. Sull'albero con radice , figli , e figlio di : preorder e postorder .
Sull'albero di riferimento 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 → (; figli di ; figli di ; figlio di ) la traccia è:
| visita | sequenza | come si ottiene |
|---|---|---|
| preorder | si visita , poi tutto (che a sua volta inizia da , poi , poi ), poi | |
| postorder | si visitano prima i sottoalberi: finisce con dopo e (cioè ), poi , infine |
In entrambi i casi ogni nodo compare una sola volta e è primo nel preorder e ultimo nel postorder. La ricorsione è quella di 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 →: lo stack contiene il cammino dalla radice al nodo corrente, quindi occupa .
Complessità
Formula (complessità di una visita). 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 →: ogni nodo non radice ha un solo padre, quindi gli archi sono ):
Se ogni visita costa si ha ; se per ogni si ha .
Esempio. In un albero con nodi , quindi .
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.sizeIl padre ha bisogno dei risultati dei figli esattamente come in una sommatoriaIl simbolo di sommatoria, le sue proprietà (linearità, additività, cambio di indice) e le somme notevoli di Gauss e geometrica.Sommatorie → annidata: il totale di un nodo è la sua dimensione propria più i totali dei sottoalberi dei figli. Esempio: /(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 (in un albero ridotto a un cammino le profondità sono e la somma è , vedi SommatorieIl simbolo di sommatoria, le sue proprietà (linearità, additività, cambio di indice) e le somme notevoli di Gauss e geometrica.Sommatorie →). Sull'albero di riferimento il preorder con assegna , poi , , , , . - 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)
Definizione (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. La differenza di profondità è un valore assolutoIl valore assoluto è la distanza di un numero da zero: definizione, proprietà, disuguaglianza triangolare e disequazioni con i moduli.Valore assoluto (modulo) → perché non si sa quale dei due nodi sia più profondo. Perché funziona: dopo aver portato i due nodi alla stessa profondità, i loro antenati alla stessa profondità sono confrontabili e il primo in comune che si incontra salendo è, per definizione, il più profondo tra quelli comuni.
Traccia su nell'albero di riferimento: , , differenza ; sale di un passo a (profondità ); ora , salgono insieme: , ; coincidono, quindi .
Esempio. Nell'albero con radice , figli , e figlio di : e .
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 sottoalberi dei figli. Postorder: prima i sottoalberi dei figli, poi . Sono schemi generali da adattare alla visita (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 →). Chiamata iniziale
preorder(T, T.root()). - Esempio: con radice , figli , e figlio di : preorder ; postorder . Sull'albero di riferimento: preorder ; postorder . è primo nel preorder e ultimo nel postorder.
- Costo: la chiamata su costa , con figli e costo della visita; con il totale è . Con è . Esempio: con , .
- Preorder, indice di un libro: capitoli e sezioni nell'ordine di lettura; è anche il modo in cui i file system mostrano cartelle e file.
- Postorder, spazio occupato (
diskSpace): il padre somma i totali dei figli, che devono essere già calcolati. Esempio:/(1K) condocs(1K, file 3K e 10K) esrc(1K, filemain.c5K e cartellalib1K conutil.c4K):docs,lib,src,/K. Costo . - Altezza
v.height: postorder conv.height <- max(v.height, 1 + w.height)per ogni figlio. - Profondità
v.depth: preorder con la profondità passata come parametro:visita(T,v,d)ponev.depth <- de chiamavisita(T,w,d+1)sui figli; . Chiamaredepthda ogni nodo costerebbe fino a su un cammino. - LCA (antenato comune più basso): si calcolano e ; il nodo più profondo sale di passi, poi entrambi salgono insieme finché coincidono. Costo . Traccia di : , ; sale a profondità , poi e : risultato .
- Compatibilità preorder/postorder: se precede nel preorder e lo segue nel postorder, è antenato di ; se precede in entrambi, sta a sinistra di . Serve per l'Esercizio 6 · preorder e postorder compatibili.
- Errori tipici: preorder dove serve il postorder (il padre ha bisogno dei figli); costo scritto come invece di ;
depthchiamato da ogni nodo.
Esercizi su questo argomento
- Esercizio 6 · preorder e postorder compatibili
- Esercizio 8 · indici del level numbering
- Esercizio 11 · massimo beneficio lungo un cammino radice-foglia
- Esercizio 16 · alberi binari di ricerca, inserimenti e visita inorder
- Esercizio 17 · terremoto forte recente in un albero di ricerca
- Esercizio 26 · C, pulizia del file system (albero binario)