Alberi binari
In questa pagina 5
Un albero binario è un albero ordinato (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 →) in cui ogni nodo interno ha al più 2 figli, ogni nodo non radice è etichettato come figlio sinistro o destro del padre, e il figlio sinistro precede il destro. Se c'è un solo figlio si assume sia il sinistro. Il sottoalbero sinistro (destro) di è quello radicato nel suo figlio sinistro (destro).
Un albero binario è proprio (o pieno, full) se ogni nodo interno ha esattamente 2 figli.
interfaccia BinaryTree estende Tree:
left(p) figlio sinistro di p (null se non esiste)
right(p) figlio destro di p (null se non esiste)
sibling(p) fratello di p (null se non esiste)Relazioni in un albero binario proprio non vuoto
Siano i nodi, le foglie (quindi i nodi interni) e l'altezza.
| # | Relazione |
|---|---|
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 |
Le 3, 4 e 5 sono corollari della 1 e della 2.
Dimostrazione della 1 (induzione sul numero di nodi interni ). Se l'albero è una sola foglia: . Altrimenti la radice è interna con sottoalberi propri di nodi interni, . Per ipotesi , , quindi .
Dimostrazione di (induzione sull'altezza). Base : una sola foglia, . Passo: di altezza ha radice con sottoalberi di altezze , e . Per ipotesi , quindi .
Dimostrazione di (induzione sull'altezza). Base : . Passo: uno dei sottoalberi ha altezza e per ipotesi almeno foglie; l'altro ha almeno foglia; in totale .
Corollari. Dalla 1, , quindi (relazione 3) e , da cui (relazione 4). Da si ottiene e da si ottiene (relazione 5).
Casi estremi. : albero perfetto (tutti i livelli pieni), , . : ogni nodo interno ha almeno una foglia come figlio ("a pettine"), , . Quindi in un albero binario proprio l'altezza è almeno logaritmica ma può essere lineare nel numero di nodi. (Verifica: le relazioni 1-5 sono state controllate su 2000 alberi casuali.)
Visita inorder
Oltre a preorder e postorder (vedi Visite di alberiVisite in preorder e postorder come schemi generali (template) da adattare; complessità Theta(n + somma dei costi di visita) perché la somma dei figli è n-1; esempi (indice di un libro, spazio occupato in un file system); profondità con il preorder, altezza con il postorder; antenato comune più basso.Visite di alberi →), negli alberi binari si ha la visita inorder: prima il figlio sinistro, poi il padre, poi il figlio destro.
Algoritmo inorder(T, v)
if T.left(v) != null then inorder(T, T.left(v))
visita v
if T.right(v) != null then inorder(T, T.right(v))Costo come le altre visite. In un albero binario di ricerca la visita inorder tocca le chiavi in ordine crescente (vedi Alberi binari di ricercaAlbero binario di ricerca come albero binario proprio con entry nei nodi interni e foglie vuote; inorder crescente; TreeSearch; get, put e remove (due casi, con predecessore inorder) in Theta(h); altezza fino a n-1; esempi di inserimenti; alberi aumentati con campi size e max e algoritmi di conteggio e interrogazione in O(h).Alberi binari di ricerca →).
Espressioni aritmetiche e parse tree
Un'espressione fully parenthesized in notazione infissa è una costante/variabile oppure con fully parenthesized e Op binario. Il parse tree è l'albero binario proprio che ha nelle foglie costanti e variabili, nei nodi interni gli operatori, con sottoalbero sinistro e destro uguali ai parse tree di ed ; le parentesi sono implicite nella struttura. Con operatori unari l'albero non è più proprio.
Esempio: ha radice , figlio sinistro con foglie , figlio destro con foglie e il nodo con foglie . I compilatori costruiscono il parse tree e lo usano per controllo dei tipi, ottimizzazioni e generazione del codice.
Valutazione, postorder: ogni operatore ha bisogno dei valori dei figli.
Algoritmo evaluateExpression(T, v)
Input: parse tree T, nodo v Output: valore dell'espressione di T_v
if T.isInternal(v) then
op <- v.getElement()
x <- evaluateExpression(T, T.left(v))
y <- evaluateExpression(T, T.right(v))
return x op y
else return v.getElement()Costo ( costante). Con l'esempio vale .
Esempio: heightSum
La heightsum di un albero binario proprio è la somma delle altezze di tutti i suoi nodi. Con la sola somma restituita da ogni sottoalbero non si potrebbe calcolare l'altezza del padre; si restituisce quindi una coppia (somma, altezza):
Algoritmo heightSum(T, v)
Input: v in T Output: (somma delle altezze dei nodi di T_v, altezza di v)
if T.isExternal(v) then return (0, 0)
(sL, hL) <- heightSum(T, T.left(v))
(sR, hR) <- heightSum(T, T.right(v))
h <- max(hL, hR) + 1
return (sL + sR + h, h)Costo (postorder). Per l'albero con radice che ha un figlio sinistro con due foglie e un figlio destro con una foglia e un nodo con due foglie, le altezze dei nodi interni sono e la heightsum è . Lezione generale: per ottenere una strategia efficiente conviene spesso calcolare un'informazione più ricca di quella richiesta (qui l'altezza oltre alla somma).
Errori comuni
- Applicare le relazioni 1-5 ad alberi non propri: valgono (con altre costanti) solo per alberi propri.
- Confondere (massimo numero di foglie) con (massimo numero di nodi).
- Usare un preorder per valutare un'espressione: i valori dei figli servono prima.
- Calcolare
heightSumchiamandoheightda ogni nodo: diventa quadratico nel caso pessimo.
Versione ripasso
- Albero binario: ogni nodo interno ha figli, ordinati (sinistro prima del destro); proprio (full): esattamente figli per nodo interno. Interfaccia:
left,right,sibling(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 →). - Relazioni (proprio, nodi, foglie, altezza ): (1) ; (2) ; (3) ; (4) ; (5) .
- Prove:
- (1) induzione sui nodi interni: ;
- : (altezza );
- : un sottoalbero ha altezza ( foglie), l'altro foglia;
- (3), (4), (5) da e .
- Estremi: perfetto; a pettine. Altezza almeno logaritmica, al più lineare.
- Inorder: sinistro, padre, destro; ; in un albero di ricerca dà le chiavi crescenti (vedi Alberi binari di ricercaAlbero binario di ricerca come albero binario proprio con entry nei nodi interni e foglie vuote; inorder crescente; TreeSearch; get, put e remove (due casi, con predecessore inorder) in Theta(h); altezza fino a n-1; esempi di inserimenti; alberi aumentati con campi size e max e algoritmi di conteggio e interrogazione in O(h).Alberi binari di ricerca →).
- Parse tree: foglie = operandi, interni = operatori;
evaluateExpressionin postorder, (vedi Visite di alberiVisite in preorder e postorder come schemi generali (template) da adattare; complessità Theta(n + somma dei costi di visita) perché la somma dei figli è n-1; esempi (indice di un libro, spazio occupato in un file system); profondità con il preorder, altezza con il postorder; antenato comune più basso.Visite di alberi →). - heightSum: postorder che restituisce (somma, altezza), ; conviene calcolare un'informazione più ricca.
- Parse tree: con vale ;
evaluateExpression: nodo interno ⇒x <- eval(left),y <- eval(right),return x op y; foglia ⇒ il suo valore. - heightSum:
(sL,hL) <- heightSum(left),(sR,hR) <- heightSum(right),h <- max(hL,hR)+1,return (sL+sR+h, h); foglia . Esempio: altezze interne ⇒ . - Errori: relazioni usate su alberi non propri; foglie contro nodi; preorder per valutare un'espressione.