Dati e Algoritmi 1: A. Pietracaprina Alberi Binari 1 Alberi Binari Definizione Un alberto binario T è un albero ordinato tale che • ogni nodo interno ha ≤ 2 figli • ogni nodo non radice è etichettato come figlio sinistro (sx) o destro (dx) di suo padre • figlio sx viene prima di figlio dx nell’ordinamento Nota A meno di casi particolari assumiamo che se c’è un solo figlio, questo sia il figlio sx. 2 Alberio Binario: Esempio e Terminologia v" so#oalbero)sx!di!v" so#oalbero)dx!di!v" 3 Alberi Binari Propri Definizione Albero Binario Proprio T : albero binario tale che: • ogni nodo interno ha esattamente 2 figli Albero'Binario' Albero'Binario'Proprio' Osservazione In letteratura gli alberi propri sono anche chiamati pieni In inglese (e.g., sul libro): • proprio = proper • pieno = full 4 Dal libro: Inoltre noi includiamo nell’interfaccia la dichiarazione dei metodi: • boolean hasLeft(Position<E> v); • boolean hasRight(Position<E> v); 5 Proprietà di un albero binario proprio T non vuoto n = numero di nodi in T m = numero di foglie in T n − m = numero di nodi interni in T h = altezza di T Proprietà: 1 m =n−m+1 2 h + 1 ≤ m ≤ 2h 3 h ≤ n − m ≤ 2h − 1 4 2h + 1 ≤ n ≤ 2h+1 − 1 5 log2 (n + 1) − 1 ≤ h ≤ n−1 2 6 Osservazioni • Proprietà 3),4) e 5) sono corollari di 1) e 2) • in [GTG14]: • nE = m, nI = n − m • Proprietà 1): Proposition 8.8 ⇒ Esercizio R-8.8 • Proprietà 2),3),4) e 5): Proposition 8.7 ⇒ Esercizio R-8.7 (Nel testo la proposizione enuncia proprietà analoghe anche per alberi non propri.) 7 Dimostrazione di 1) m =n−m+1 Dimostrazione Per induzione su h ≥ 0 • k = 0, h0 = 0 • base: h = 0 ⇒ m = 1 e n = 1 ⇒ OK • passo induttivo: fissiamo h ≥ 0 Hp. induttiva: proprietà vale ∀ albero di altezza h0 ≤ h Consideriamo T di altezza h + 1 ≥ 1 r" h1" T1" h2" h+1" T2" h+1="max${"h1","h2"}"+"1""(h="max${"h1","h2"}")" 8 Proprietà: m = n − m + 1 Dimostrazione (continua) r" h1" T1" h2" h+1" T2" h+1="max${"h1","h2"}"+"1""(h="max${"h1","h2"}")" mi = num. foglie in Ti , i = 1, 2 ni = num. nodi in Ti , i = 1, 2 ⇒ m = m1 + m2 e n = n1 + n2 + 1 m = m1 + m2 =(hp.ind.) n1 − m1 + 1 + n2 − m2 + 1 = n − m + 1 9 Dimostrazione in [GTG14] Proprietà: m = n − m + 1 Dimostrazione Due casi: • n = 1: banale • n > 1 sino a quando l’albero ha ≥ 2 nodi eseguo la seguente operazione: Ottengo ancora un albero binario proprio con n − 2 nodi Quando arrivo ad un albero binario con 1 solo nodo (foglia) mi fermo. Avrò eliminato m − 1 foglie e m − 1 nodi interni, e mi è rimasta una foglia ⇒ n = 2m − 2 + 1 ⇒ m = n − m + 1 10 Dimostrazione di 2) h + 1 ≤ m ≤ 2h , cioè: m ≤ 2h e m ≥ h + 1 Dimostriamo m ≤ 2h Dimostrazione Per induzione su h ≥ 0 • base: h = 0 ⇒ m = 1: OK • passo induttivo: fissiamo h ≥ 0 Hp. induttiva: proprietà vale ∀ albero di altezza h0 ≤ h Consideriamo T di altezza h + 1 ≥ 1 r" h1" T1" h2" h+1" T2" h+1="max${"h1","h2"}"+"1""(h="max${"h1","h2"}")" 11 Consideriamo m ≤ 2h Dimostrazione (continua) r" h1" T1" h2" h+1" T2" h+1="max${"h1","h2"}"+"1""(h="max${"h1","h2"}")" mi = num. foglie in Ti , i = 1, 2 ⇒ m = m1 + m2 m = m1 + m2 ≤(hp.ind.) 2h1 + 2h2 ≤ 2 × 2h = 2h+1 m ≥ h + 1: ESERCIZIO PER CASA Nota Un albero binario proprio ha altezza h = Ω (log m). E se non è proprio? ESERCIZIO 12 Alberi Binari Propri Estremi h" "m"="h"+1"" "n"–"m"="h" "n"="2h"+1"" "h"="(n+1)/2" !m!=!2h! !n!=!2h+1!(!1!! !n!–!m!=!2h(1! !h!=!log2(n+1)!(!1! 2h! 13 Alberi Binari e Espressioni Aritmetiche Definizione Una espressione fully parenthesized (notazione infissa) è: • una costante o variabile a, oppure • (E1 Op E2 ), dove E1 , E2 sono espressioni fully parenthesized e Op è un operatore binario Esempio ((15 + x) ∗ (7 − (9 ÷ 3))) 14 Albero binario associato ad una espressione (Parse Tree) Definizione . • nodi foglia = costanti o variabili . • nodi interni = operatori • ogni sottoalbero rappresenta una espressione, e la radice è associata al valore dell’espressione Esempio *" &" +" 15" N.B.: è un albero binario proprio x" 7" ÷" 9" 3" 15 Se si ammettono operatori unari, l’albero non è più proprio Esempio: −15 ∗ (3 + 7) *" +" !" 15" 3" 7" Osservazione Nei compilatori Espressione) (notazione) infissa))) ParseTree) Sequenza)di)operazioni) facilmente)calcolabile)a) run;me)(e.g.,)notazione) pos?issa)) Type)checking,)verifiche,) oDmizzazioni) 16 Valutazione dell’espressione Algoritmo evaluateExpression(T ,v ) Input: Parse Tree T for E , v ∈ T Output: valore associato al sottoalbero Tv if T .isInternal(v ) then op ← v .element(); x ← evaluateExpression(T ,T .left(v )); y ← evaluateExpression(T ,T .right(v )); return x op y ; else return v .element(); Osservazioni • Visita in postorder • Chiamata iniziale: evaluateExpression(T , T .root()). • Complessità = O (n), dove n è il numero di nodi in T 17 Definizione Una espressione in notazione postfissa è: • una costante o variabile a, oppure • E1 E2 Op, dove E1 , E2 sono espressioni in notazione postfissa e Op è un operatore binario Esempio Espressione f.p. in notazione infissa: ((15 + x) ∗ (7 − (9 ÷ 3))) *" &" +" 15" x" 7" ÷" 9" 3" Espressione in notazione postfissa (ottenibile dalla visita postorder del parse tree): 15 x + 7 9 3 ÷ − ∗ Osservazione I compilatori spesso rappresentano le espressioni nel codice eseguibile con notazione postfissa (esempio: Java bycode usa la notazione postfissa). 18 Visita inorder Idea: prima il figlio sx, poi il padre, poi il figlio dx Algoritmo inorder(T ,v ) Input: T , v ∈ T Output: visita inorder di Tv if T .hasLeft(v ) then inorder(T ,T .left(v ); visita v ; if T .hasRight(v ) then inorder(T ,T .right(v ); Osservazioni: • Chiamata iniziale: inorder(T , T .root()). • Complessità = O (n), dove n è il numero di nodi in T . Stessa analisi delle visite in preorder/postorder. • La visita inorder è usata, ad esempio, negli alberi binari di ricerca, per stampare i contenuti dei nodi in ordine crescente di chiave. 19 Riferimenti dal testo [GTG14]: • Par. 8.2 • Par. 8.4.3-8.4.5 20 Caso di studio 3: Decision tree • Machine Learning: Area dell’informatica che si occupa dello sviluppo di algoritmi e modelli per analizzare dati allo scopo di scoprire nuova informazione e fare previsioni, a supporto di processi decisionali. • Supervised learning: si impara da dati storici per fare previsioni future. I dati da trattare sono record caratterizzati da un insieme di feature (o attributi) e una classe. Il problema della classification consiste nell’analizzare un tranining set di record classificati per trovare un modello che predica una classe a partire dalle feature. Il modello è poi applicato successivamente per assegnare una classe a nuovi record non classificati. • I Decision tree sono tra i modelli più utilizzati e importanti nell’ambito del machine learning. Forniscono buona accuratezza, sono semplici da derivare, e sono descrittivi. Inoltre possono essere utilizzati in ensemble. 21 Caso di studio 3: Decision tree (continua) 22 Esercizi Esercizio 1 Sia T il parse tree associato a una espressione aritmetica E fully parenthesized in notazione infissa che usa solo operatori binari. Sviluppare un algoritmo ricorsivo infix che ricavi l’espressione E a partire dal parse tree T , e analizzarne la complessità in tempo. Esempio L’espressione associata è: *" 15" ((15 + x) ∗ (7 − (9 ÷ 3))) &" +" x" 7" ÷" 9" 3" La visita inorder produrrebbe la sequenza: 15 + x ∗ 7 − 9 ÷ 3. Basta quindi aggiungere le opportune parentesi. 23 L’algoritmo è il seguente (si usa + per indicare la concatenazione di stringhe): Algoritmo infix(T,v) input Parse tree T , nodo v ∈ T output espressione Ev rappresentata da Tv if (T.isExternal(v)) then return v.element() else return ’(’+infix(T,T.left(v))+v.element()+infix(T,T.right(v))+’)’ • Per generare l’espressione completa associata a T , basterà invocare l’algoritmo con v=T.root(). • Complessità è lineare nel numero di nodi del (sotto)albero sul quale è invocato. (Come una qualsiasi visita.) 24 Esercizio 2 Sia T un albero binario proprio. Si definisca la heightsum di T come la somma delle altezze di tutti i suoi nodi. (Si ricordi che l’altezza di una foglia è 0, e quella di un nodo interno è 1+la massima altezza dei suoi figli.) 1 Per calcolare la heightsum dell’albero, si sviluppi un algoritmo ricorsivo heightSum(T , v ) che calcola la heightsum del sottoalbero Tv , per un nodo arbitrario v . 2 Analizzare la complessità di heightSum(T,T.root()). L’idea è quella di adattare la visita in postorder facendo restituire a heightSum(T , v ) non solo la somma delle altezze dei nodi di Tv , ma anche l’altezza di v . Altrimenti, dopo aver calcolato ricorsivamente le heightsum dei sottoalberi figli non si potrebbe calcolare l’altezza di v e quindi la heightsum di Tv . Questo è un esempio in cui calcolando un’informazione più ricca (somma delle altezze e altezza) si ottiene una strategia algoritmica più efficiente. 25 1 L’algoritmo è il seguente: Algoritmo heightSum(T , v ) input v ∈ T output somma altezze nodi di Tv , 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 s ← sL + sR + h return (s, h) 2 • Prima invocazione: heightSum(T ,T .root()) • Complessità: O (n), come visita in postorder. 26 Esercizio 3 (C-8.41 in [GTG14]) Si consideri un albero binario proprio T . Progettare un metodo preorderNext(v) che restituisce il nodo visitato dopo v nella visita in preorder di T , o null, se v è l’ultimo nodo visitato. Osservazioni • Se v è un nodo interno, allora il suo successore nella visita in preorder è il suo figlio sinisitro. • Se v è una foglia, dobbiamo risalire sino a trovare il primo antenato u (cioè l’antenato più profondo) tale che v sta nel sottoalbero sinistro di u. In questo caso, il successore di v nella visita in preorder è il figlio destro di u. Se tale antenato u non esiste, significa che v è l’ultimo nodo visitato dalla vista in preorder di T 27 L’algoritmo è il seguente: Algoritmo preorderNext(v ) input v ∈ T output successore di v in preorder (o null se non esiste) if (T .isInternal(v )) then return T .left(v ) while (!T.isRoot(v )) do { if (v =T .left(T .parent(v ))) then return T .right(T .parent(v )) else v ← T .parent(v ) } return null Analisi • Numero di iterazioni del while ≤ profondità di v . • In ciascuna iterazione Θ (1) operazioni. • Per il resto l’algoritmo esegue Θ (1) operazioni. • Dato che la profondità di v è al più l’altezza h di T , concludiamo che la complessità è O (h). 28 Esercizio 4 Dimostrare che in un albero binario proprio T con m foglie e altezza h vale che m ≥ h + 1. Cercare due prove diverse: una con l’induzione e una senza. Prova per induzione sull’altezza h dell’albero. • BASE: la proprietà è vera per un albero di altezza h = 0, che quindi ha un solo nodo (foglia) e m = 1. • PASSO INDUTTIVO: Supponiamo che la proprietà sia vera per alberi di altezza al più h, con h ≥ 0 fissato, e consideriamo un albero T di altezza h + 1 ≥ 1. Chiaramente la radice di T deve essere un nodo interno e uno dei due suoi sottoalberi deve avere altezza h. Il sottoalbero di altezza h deve avere almeno h + 1 foglie (hp. induttiva), mentre l’altro deve avere almeno una foglia, per un totale di almeno h + 2 foglie. 29 Prova senza induzione. Un albero di altezza h deve avere una foglia v di profondità h. Sia r = v0 → v1 → · · · → vh = v il percorso dalla radice a tale foglia. Gli h sottoalberi radicati nei fratelli di vi , per 1 ≤ i ≤ h, sono disgiunti e devono avere almeno una foglia ciascuno. Insieme a v questo somma a un totale di almeno h + 1 foglie. 30 Riepilogo sugli alberi (binari e non) • Definizioni: albero, albero binario (proprio), antenati, discendenti, sottoalbero, profondità di un nodo, altezza di un nodo e di un albero. • Relazione tra altezza di un albero e profondità delle foglie • Algoritmi per il calcolo della profondità/altezza di un nodo e dell’altezza di un albero • Relazioni tra numero di nodi interni, foglie e altezza in un albero binario proprio • Visite: preorder, postorder, inorder e loro applicazioni • Algoritmi di vista come template generali 31 Esempio domande prima parte • Come sono definiti gli antenati di un nodo in un albero T ? • Specificare la relazione di ricorrenza che definisce la complessità dell’algoritmo ricorsivo per il calcolo della profondità di un nodo. • Descrivere l’algoritmo per il calcolo dell’altezza di un albero T indicandone, senza dimostrarla, la complessità. • Dimostrare che l’altezza di un albero è uguale alla massima profondità di una sua foglia • Sia T un albero binario proprio con n − m nodi interni e m foglie (n nodi totali). Dimostrare che m = n − m + 1. 32