Salta al contenuto
Note per Studenti Alberi binari

Alberi binari

In questa pagina 5

Definizione (albero binario e albero binario proprio). 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 vv è quello radicato nel suo figlio sinistro (destro).

Un albero binario è proprio (o pieno, full) se ogni nodo interno ha esattamente 2 figli. Un albero binario è un caso particolare di albero (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 →) e di grafo senza cicli (Grafi - definizioni e proprietàGrafo G=(V,E) diretto e non diretto, grafo semplice e pesato; incidenza, adiacenza, grado; cammini, cicli, sottografi, grafi connessi e componenti connesse; alberi liberi, foreste, spanning tree e spanning forest; proprietà con dimostrazioni (somma dei gradi = 2m, m <= n(n-1)/2, alberi m = n-1, connessi m >= n-1, foreste m <= n-1).Grafi - definizioni e proprietà →); le interfacce sono ADT (Tipi di dato astrattiUn ADT è definito dalle operazioni e dal loro comportamento, non dalla rappresentazione; interfaccia e implementazione; realizzazione in Python con le classi e in C con header e tipo opaco; esempio di un ADT Frazione.Tipi di dato astratti →).

Esempio. Un albero con la radice, un figlio sinistro con due foglie e un figlio destro foglia è binario proprio: ogni nodo interno ha 22 figli e n=5n=5.

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 nn i nodi, mm le foglie (quindi n−mn - m i nodi interni) e hh l'altezza.

Teorema (relazioni in un albero binario proprio)

# Relazione
1 m=(n−m)+1m = (n - m) + 1
2 h+1≤m≤2hh + 1 \le m \le 2^h
3 h≤n−m≤2h−1h \le n - m \le 2^h - 1
4 2h+1≤n≤2h+1−12h + 1 \le n \le 2^{h+1} - 1
5 log⁡2(n+1)−1≤h≤n−12\log_2(n+1) - 1 \le h \le \frac{n-1}{2}

Esempio. Albero perfetto di altezza h=2h=2: n=23−1=7n=2^3-1=7, m=22=4m=2^2=4 foglie e n−m=3=m−1n-m=3=m-1.

Le 3, 4 e 5 sono corollari della 1 e della 2. Valori per h=0,…,4h = 0,\dots,4 (intervalli possibili per foglie e nodi):

hh foglie mm: da h+1h+1 a 2h2^h nodi nn: da 2h+12h+1 a 2h+1−12^{h+1}-1
0 1 1
1 2 3
2 da 3 a 4 da 5 a 7
3 da 4 a 8 da 7 a 15
4 da 5 a 16 da 9 a 31

Dimostrazione della 1 (induzione sul numero di nodi interni i=n−mi = n - m). Se i=0i = 0 l'albero è una sola foglia: m=1=0+1m = 1 = 0 + 1. Altrimenti la radice è interna con sottoalberi propri T1,T2T_1, T_2 (non vuoti, perché l'albero è proprio) di i1,i2i_1, i_2 nodi interni, i=i1+i2+1i = i_1 + i_2 + 1 (i nodi interni dei due sottoalberi più la radice stessa). Per ipotesi m1=i1+1m_1 = i_1 + 1, m2=i2+1m_2 = i_2 + 1, quindi m=m1+m2=i1+i2+2=i+1m = m_1 + m_2 = i_1 + i_2 + 2 = i + 1. □\square

Dimostrazione di m≤2hm \le 2^h (induzione sull'altezza). Base h=0h = 0: una sola foglia, 1=201 = 2^0. Passo: TT di altezza h+1h+1 ha radice con sottoalberi T1,T2T_1, T_2 di altezze h1,h2≤hh_1, h_2 \le h, e h+1=max⁡(h1,h2)+1h + 1 = \max(h_1, h_2) + 1. Per ipotesi mi≤2hi≤2hm_i \le 2^{h_i} \le 2^h (la seconda disuguaglianza perché hi≤hh_i \le h e 2x2^x è crescente: Esponenziale e logaritmoLa funzione esponenziale a^x (base positiva diversa da 1) e la sua inversa, il logaritmo in base a, con grafici e proprietà.Esponenziale e logaritmo →), quindi m=m1+m2≤2⋅2h=2h+1m = m_1 + m_2 \le 2 \cdot 2^h = 2^{h+1}. □\square

Dimostrazione di m≥h+1m \ge h + 1 (induzione sull'altezza). Base h=0h = 0: m=1m = 1. Passo: uno dei sottoalberi ha altezza hh e per ipotesi almeno h+1h + 1 foglie; l'altro ha almeno 11 foglia (ogni albero non vuoto ne ha una); in totale m≥(h+1)+1=h+2m \ge (h+1) + 1 = h + 2, che è la tesi per un albero di altezza h+1h+1. □\square

Corollari. Dalla 1, n−m=m−1n - m = m - 1, quindi h≤n−m≤2h−1h \le n - m \le 2^h - 1 (relazione 3) e n=m+(n−m)=2m−1n = m + (n - m) = 2m - 1, da cui 2(h+1)−1=2h+1≤n≤2h+1−12(h+1) - 1 = 2h + 1 \le n \le 2^{h+1} - 1 (relazione 4). Da n≤2h+1−1n \le 2^{h+1} - 1 si ottiene h≥log⁡2(n+1)−1h \ge \log_2(n+1) - 1 e da n≥2h+1n \ge 2h + 1 si ottiene h≤n−12h \le \frac{n-1}{2} (relazione 5). Passaggi: n≤2h+1−1  ⟺  n+1≤2h+1n \le 2^{h+1}-1 \iff n+1 \le 2^{h+1}, e applicando log⁡2\log_2 (funzione crescente, Esponenziale e logaritmoLa funzione esponenziale a^x (base positiva diversa da 1) e la sua inversa, il logaritmo in base a, con grafici e proprietà.Esponenziale e logaritmo →) a entrambi i membri log⁡2(n+1)≤h+1\log_2(n+1) \le h+1; analogamente 2h+1≤n  ⟺  h≤n−122h+1 \le n \iff h \le \frac{n-1}{2}. Il grafico mostra i due limiti: ogni albero binario proprio con nn nodi ha l'altezza tra le due curve.

Grafico interattivo: Altezza di un albero binario proprio con n nodi: limite inferiore log2(n+1) − 1 (albero perfetto) e limite superiore (n − 1)/2 (albero a pettine); per n = 31 l'altezza è tra 4 e 15

Casi estremi. m=2hm = 2^h: albero perfetto (tutti i livelli pieni), n=2h+1−1n = 2^{h+1} - 1, h=log⁡2(n+1)−1h = \log_2(n+1) - 1. m=h+1m = h + 1: ogni nodo interno ha almeno una foglia come figlio ("a pettine"), n=2h+1n = 2h + 1, h=n−12h = \frac{n-1}{2}. 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

Definizione (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.

Esempio. Sull'albero con radice ++ e foglie 33 e 44 la visita inorder dà 3,+,43,+,4.

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 Θ(n+∑tu)\Theta(n + \sum t_u) 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

Definizione (parse tree di un'espressione). Un'espressione fully parenthesized in notazione infissa è una costante/variabile aa oppure (E1 Op E2)(E_1\ \text{Op}\ E_2) con E1,E2E_1, E_2 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 E1E_1 ed E2E_2; le parentesi sono implicite nella struttura. Con operatori unari l'albero non è più proprio. Le espressioni aritmetiche e la precedenza degli operatori sono quelle di Tipi numerici ed espressioniint, float, complex e bool; operatori aritmetici con divisione intera e modulo; precedenza; conversioni di tipo; modulo math e arrotondamenti.Tipi numerici ed espressioni → (Fondamenti di Informatica); la costruzione del parse tree è una fase della compilazione (Paradigmi di programmazione e linguaggiAlgoritmo e programma; paradigmi imperativo, procedurale, a oggetti, funzionale e dichiarativo; compilazione e interpretazione; confronto tra Python e C.Paradigmi di programmazione e linguaggi →, Dal programma C all'eseguibile ARMCatena di traduzione compilatore, assemblatore, linker e loader; file oggetto, tabella dei simboli e rilocazione; collegamento statico e dinamico; organizzazione della memoria di un processo; chiamare funzioni assembly da C e viceversa, con esempio.Dal programma C all'eseguibile ARM →).

Esempio. ((15+x)⋅(7−(9÷3)))((15+x)\cdot(7-(9\div3))) con x=5x=5 vale 20⋅4=8020\cdot4=80.

Esempio: ((15+x)⋅(7−(9÷3)))((15 + x) \cdot (7 - (9 \div 3))) ha radice ⋅\cdot, figlio sinistro ++ con foglie 15,x15, x, figlio destro −- con foglie 77 e il nodo ÷\div con foglie 9,39, 3. 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 Θ(n)\Theta(n) (tut_u costante). Con x=5x = 5 l'esempio vale (15+5)⋅(7−9/3)=20⋅4=80(15+5)\cdot(7 - 9/3) = 20 \cdot 4 = 80. Traccia del postorder (i valori risalgono dalle foglie): 15+x=2015 + x = 20; 9÷3=39 \div 3 = 3; 7−3=47 - 3 = 4; 20⋅4=8020 \cdot 4 = 80. La visita inorder dello stesso albero dà 15,+,x,⋅,7,−,9,÷,315,+,x,\cdot,7,-,9,\div,3, cioè l'espressione senza parentesi: perde l'informazione sull'ordine delle operazioni, che solo la struttura dell'albero conserva.

Esempio: heightSum

Definizione (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)

Esempio. In un albero con la radice, 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 3,1,2,13,1,2,1 e la heightsum vale 77.

Costo Θ(n)\Theta(n) (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 3,1,2,13, 1, 2, 1 e la heightsum è 77. Traccia dell'algoritmo, coppia (somma, altezza) di ogni sottoalbero: ogni foglia dà (0,0)(0,0); il figlio sinistro della radice, con due foglie, dà h=max⁡(0,0)+1=1h=\max(0,0)+1=1 e somma 0+0+1=10+0+1=1, cioè (1,1)(1,1); il nodo con due foglie nel sottoalbero destro dà ancora (1,1)(1,1); il figlio destro, con una foglia (0,0)(0,0) e quel nodo (1,1)(1,1), dà h=max⁡(0,1)+1=2h=\max(0,1)+1=2 e somma 0+1+2=30+1+2=3, cioè (3,2)(3,2); la radice, con (1,1)(1,1) a sinistra e (3,2)(3,2) a destra, dà h=max⁡(1,2)+1=3h=\max(1,2)+1=3 e somma 1+3+3=71+3+3=7, cioè (7,3)(7,3). 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 2h2^h (massimo numero di foglie) con 2h+1−12^{h+1} - 1 (massimo numero di nodi).
  • Usare un preorder per valutare un'espressione: i valori dei figli servono prima.
  • Calcolare heightSum chiamando height da ogni nodo: diventa quadratico nel caso pessimo.

Versione ripasso

# Relazione
1 m=(n−m)+1m = (n - m) + 1
2 h+1≤m≤2hh + 1 \le m \le 2^h
3 h≤n−m≤2h−1h \le n - m \le 2^h - 1
4 2h+1≤n≤2h+1−12h + 1 \le n \le 2^{h+1} - 1
5 log⁡2(n+1)−1≤h≤n−12\log_2(n+1) - 1 \le h \le \frac{n-1}{2}

Esercizi su questo argomento

Teoria collegata