Salta al contenuto
Note per Studenti Esercizio 9 · foglia più profonda di un albero binario proprio

Esercizio 9foglia più profonda di un albero binario proprio

Esame
In questa pagina 6

Testo (esempio di tema d'esame e scritto del 09/09/2025, seconda parte, esercizio 1, 5 punti). Si vuole progettare un algoritmo ricorsivo deepLeaf per trovare la foglia più profonda in un albero binario proprio TT. Detta deepLeaf(T, v) la generica invocazione su un nodo v∈Tv \in T, per risolvere il problema si eseguirà deepLeaf(T, T.root()) (che può anche restituire informazioni aggiuntive). Si ricordi che in ogni sottoalbero TvT_v la profondità (in TvT_v) della sua foglia più profonda è pari all'altezza di TvT_v.

(a) Descrivere deepLeaf(T, v) tramite pseudocodice, specificandone con attenzione l'input e l'output. (b) Analizzare la complessità di deepLeaf(T, T.root()) in funzione del numero nn di nodi in TT.


Idea

Il suggerimento del testo: l'altezza di TvT_v è la massima profondità delle sue foglie (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 →). Quindi la foglia più profonda di TvT_v è, tra le foglie più profonde dei due sottoalberi figli, quella del sottoalbero più alto. Per decidere serve conoscere l'altezza dei due figli: si restituisce una coppia (foglia, altezza), come in heightSum (vedi Alberi binariAlbero binario e albero binario proprio; interfaccia; relazioni tra nodi, foglie e altezza (m = n-m+1, h+1 <= m <= 2^h, 2h+1 <= n <= 2^(h+1)-1) con dimostrazioni; visita inorder; parse tree e valutazione di espressioni; heightSum come esempio di calcolo di un'informazione più ricca.Alberi binari →): conviene calcolare un'informazione più ricca di quella richiesta. È una visita in postorder.

(a) Pseudocodice

Algoritmo deepLeaf(T, v)
Input: albero binario proprio T, nodo v di T
Output: coppia (w, h) con w = una foglia più profonda di T_v e h = altezza di T_v
if T.isExternal(v) then return (v, 0)
(wL, hL) <- deepLeaf(T, T.left(v))
(wR, hR) <- deepLeaf(T, T.right(v))
if hL > hR then return (wL, hL + 1)
else return (wR, hR + 1)

Chiamata: (w, h) <- deepLeaf(T, T.root()); ww è la foglia cercata e hh l'altezza dell'albero.

La coppia restituita ha lo stesso ruolo di quella di heightSum (somma, altezza): l'informazione richiesta (la foglia) non basta al padre, serve anche l'altezza. La ricorsione e la sua analisi sono quelle 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 →.

Correttezza (induzione sull'altezza, Principio di induzionePer dimostrare una proprietà per ogni n basta il passo base e il passo induttivo da n a n+1.Principio di induzione →). Foglia: l'unica foglia di TvT_v è vv stesso, a profondità 00 = altezza. Nodo interno: le foglie di TvT_v sono quelle di TLT_L e TRT_R con profondità aumentata di 11 rispetto ai sottoalberi; per ipotesi wLw_L e wRw_R sono le più profonde nei rispettivi sottoalberi, quindi la più profonda di TvT_v è quella con altezza maggiore, a profondità 1+max⁡(hL,hR)1 + \max(h_L, h_R) in TvT_v, che è l'altezza di vv.

(b) Complessità

L'albero della ricorsione ha esattamente un nodo per ogni nodo di TT; ogni chiamata, escluse le figlie, fa un numero costante di operazioni (un test, un confronto, una coppia restituita). Dunque t(n)∈Θ(n),t(n) \in \Theta(n), come una visita in postorder in cui la visita di un nodo costa Θ(1)\Theta(1).

Esempio

Albero: radice rr con figli xx e yy; xx ha figli (foglie) aa e bb; yy è foglia. deepLeaf(a) =(a,0)= (a, 0), deepLeaf(b) =(b,0)= (b, 0); in xx: hL=0≯hR=0h_L = 0 \not> h_R = 0, quindi si restituisce (b,1)(b, 1) (con la parità si sceglie il destro; si poteva scegliere aa, che è ugualmente profonda). deepLeaf(y) =(y,0)= (y, 0). In rr: hL=1>hR=0h_L = 1 > h_R = 0, quindi (b,2)(b, 2): la foglia più profonda è bb, a profondità 22. (Controllato su 500 alberi casuali: profondità della foglia restituita = massima profondità.)

Un secondo esempio più profondo: radice rr con figlio sinistro pp (interno, con figli foglie cc e dd) e figlio destro qq (interno, con figlio sinistro foglia ee e figlio destro ss interno con foglie ff e gg). Dal basso: deepLeaf(f) =(f,0)=(f,0), deepLeaf(g) =(g,0)=(g,0), in ss: (g,1)(g,1); deepLeaf(e) =(e,0)=(e,0); in qq: hL=0h_L=0, hR=1h_R=1, quindi (g,2)(g,2); in pp: (d,1)(d,1); in rr: hL=1h_L=1, hR=2h_R=2, si sceglie il destro: (g,3)(g,3). La foglia più profonda è gg, a profondità 33 (altezza dell'albero).

Alternative (peggiori)

Errori comuni

  • Restituire solo la foglia: senza l'altezza il padre non può confrontare i due figli, e si finisce a ricalcolare le altezze (quadratico).
  • Restituire solo l'altezza: si risponde a un problema diverso.
  • Scrivere hL >= hR e hL > hR indifferentemente ma non indicare cosa succede alla parità: il testo accetta una qualsiasi delle foglie più profonde, va però dichiarato.

Versione ripasso

Testo. Albero binario proprio TT: algoritmo ricorsivo deepLeaf(T, v) che trova la foglia più profonda; specificare input e output; complessità in nn.

Teoria collegata