Grundlagen der Programmierung 2 Bäume Prof. Dr. Manfred Schmidt-Schauÿ Künstliche Intelligenz und Softwaretechnologie 24. Mai 2006 Graphen Graph: Menge von Knoten undzugehörige (gerichtete oder ungerichtete) Kanten zwischen den Knoten Einige Begriffe für ungerichtete Graphen Schlingen Wege Kreise Erreichbarkeit zusammenhängend: markierter Graph Grundlagen der Programmierung 2 Kanten mit gleichem Anfangs- und Endknoten Kantenfolgen (A, B), (B, C), . . . Kantenfolgen (A, B), (B, C), . . . , (Z, A) A ist von B aus erreichbar, wenn es einen Weg von A nach B gibt alle Knoten sind von jedem Knoten aus erreichbar Knoten bzw. Kanten haben Markierungen - 1 - Bäume Baum: ein (gerichteter oder ungerichteter) Graph, zusammenhängend, ohne Kreise, mit Wurzel-Knoten Annahme: die Wurzel ist oben, Wurzel Vorgänger Blatt Tochter Nachfolger Grundlagen der Programmierung 2 - 2 - Bäume: Einige Begriffe: geordneter Baum markierter Baum Rand des Baumes binärer Baum Höhe (Tiefe) balanciert Grad eines Knoten Grad eines Baumes Grundlagen der Programmierung 2 Links-Rechts-Ordnung auf den Töchtern (Kinder,Söhne) Die Knoten haben Markierung (bzw. Kanten) Liste der Blattmarkierungen eines geordneten Baumes Jeder Knoten ist Blatt oder hat genau zwei Töchter maximale Länge eines Weges von der Wurzel zu einem Blatt bei gleichem Rand kleinste Tiefe (eines binären Baumes) Anzahl der Töchter maximaler Grad eines Knoten - 3 - Datenstruktur Baum Binäre, geordnete Bäume in Haskell: data Binbaum a = Bblatt • • • • a | Bknoten (Binbaum a) (Binbaum a) Daten (Markierungen) sind an den Blättern des Baumes Die Daten sind von gleichem Typ Jeder (innere) Knoten hat genau zwei Tochterknoten es gibt linken und rechten Tochterknoten (geordnet) Grundlagen der Programmierung 2 - 4 - Datenstruktur Baum; Beispiel Der folgende binäre Baum 1 7 3 8 4 hat eine Darstellung als Bknoten (Bknoten (Bblatt 1) (Bknoten (Bblatt 3) (Bblatt 4))) (Bknoten (Bblatt 7) (Bblatt 8)) Grundlagen der Programmierung 2 - 5 - Einige Verarbeitungsfunktionen Rand: Liste der Markierungen der Blätter: b_rand (Bblatt x) = [x] b_rand (Bknoten bl br) = (b_rand bl) ++ (b_rand br) testet, ob Element im Baum ist: b_in x (Bblatt y) = (x == y) b_in x (Bknoten bl br) = b_in x bl || b_in x br wendet eine Funktion auf alle Elemente des Baumes an, Resultat: Baum der Resultate b_map f (Bblatt x) = Bblatt (f x) b_map f (Bknoten bl br) = Bknoten (b_map f bl) Grundlagen der Programmierung 2 (b_map f br) - 6 - Einige Verarbeitungsfunktionen (2) Größe des Baumes: b_size (Bblatt x) = b_size (Bknoten bl br) = 1 1+ (b_size bl) + (b_size br) Summe aller Blätter, falls Zahlen: b_sum (Bblatt x) = x b_sum (Bknoten bl br) = (b_sum bl) + (b_sum br) Tiefe des Baumes: b_depth (Bblatt x) = 0 b_depth (Bknoten bl br) = 1 + (max (b_depth bl) (b_depth br)) Grundlagen der Programmierung 2 - 7 - Einige Verarbeitungsfunktionen (3) schnelles fold über binäre Bäume: foldbt :: (a -> b -> b) -> b -> Binbaum a -> b foldbt op a (Bblatt x) = op x a foldbt op a (Bknoten x y) = (foldbt op (foldbt op a y) x) foldbt mit optimiertem Stackverbrauch: foldbt’ :: (a -> b -> b) -> b -> Binbaum a -> b foldbt’ op a (Bblatt x) = op x a foldbt’ op a (Bknoten x y) = (((foldbt’ op) $! (foldbt’ op a y)) x) effizientere Version von b rand und b sum b_rand_eff = foldbt (:) [] b_baum_sum’ = foldbt’ (+) 0 Grundlagen der Programmierung 2 - 8 - Auswertung mit foldbt Werte foldbt (+) 0 "(((1,2),3),(4 ,5))" aus: foldbt (+) 0 "(((1,2),3),(4 ,5))" --> foldbt (+) (foldbt (+) 0 (4 ,5)) ((1,2),3) --> foldbt (+) (foldbt (+) (foldbt (+) 0 (5)) (4) ((1,2),3)) --> foldbt (+) (foldbt (+) (5+0) (4) ((1,2),3)) --> foldbt (+) (4+ (5+0)) ((1,2),3) --> foldbt (+) (foldbt (+) (4+ (5+0)) (3)) (1,2) --> foldbt (+) (3+ (4+ (5+0))) (1,2) --> foldbt (+) (foldbt (+) (3+ (4+ (5+0))) (2) (1)) --> foldbt (+) (2+ (3+ (4+ (5+0)))) (1) --> 1+ (2+ (3+ (4+ (5+0)))) Grundlagen der Programmierung 2 - 9 - Bemerkungen zu foldbt Sei tr binärer Baum mit Randliste [a1, . . . , an] (foldbt f a tr) entspricht (f a_1 (f a_2 (... (f a_n a) ...s))). D.h. foldbt entspricht foldr Grundlagen der Programmierung 2 - 10 - Vergleich von b rand und b rand eff : Nur für volle binäre Bäume: testbaum m Ergebnislisten werden voll ausgewertet Zählweise der Reduktionen wie in Hugs Grundlagen der Programmierung 2 - 11 - Analyse von b rand: #Reduktionen von (b_rand testbaum_n) Tiefe #Blätter #berechnet m 2m 2m + 2m−1 ∗ m + 3 ∗ 2m n n + n/2 ∗ log2(n) + 3 ∗ n 10 1024 9216 12 4096 40960 13 8192 86016 14 16384 180224 Grundlagen der Programmierung 2 #tatsächliche 2m + 2m−1 ∗ m + 3 ∗ 2m + 14 n + n/2 ∗ log2(n) + 3 ∗ n + 14 9230 40974 86030 180238 - 12 - Analyse von b rand eff #Reduktion von (b_rand_eff testbaum_n) Tiefe #Blätter #berechnet m 2m 2m + 2 ∗ 2m n 3n 10 1024 1072 12 4096 12288 13 8192 24576 14 16384 49152 Fazit: #tatsächliche 2m + 2 ∗ 2m + 15 3n + 15 3087 12303 24591 49167 #Red(b rand) 9230 40974 86030 180238 b rand eff ist effizienter: O(n) statt O(n ∗ log(n)). Grundlagen der Programmierung 2 - 13 - Suchbaum Effizienzsteigerung beim Zugriff auf Elemente: sortierte Liste als Baum Jeder Knoten enthält als Markierung den größten Schlüssel des linken Teilbaumes mit den kleineren Werten. Laufzeit des Zugriffs: logarithmisch in der Anzahl der Elemente Grundlagen der Programmierung 2 - 14 - Suchbaum, Beispiel 5 7 1 7 1 11 3 3 Grundlagen der Programmierung 2 5 - 15 - Haskell-Implementierung: Suchbaum data Satz a = Satz Integer a data Suchbaum a = Sblatt Int a | Sknoten (Suchbaum a) Int (Suchbaum a) | Suchbaumleer Verwendung: Suchbaum (Satz a) intial = Suchbaumleer Grundlagen der Programmierung 2 - 16 - Haskell-Implementierung: Suchbaum einfuegeDbS (Satz x sx) Suchbaumleer = Sblatt x (Satz x sx) einfuegeDbS (Satz x sx) (Sblatt k satzk) = if x < k then Sknoten (Sblatt x (Satz x sx)) x (Sblatt k satzk) else if x == k then error " schon eingetragen" else Sknoten (Sblatt k satzk) k (Sblatt x (Satz x sx)) einfuegeDbS (Satz x sx) (Sknoten l k r) = if x < k then Sknoten (einfuegeDbS (Satz x sx) l) k r else if x == k then error " schon eingetragen" else Sknoten l k (einfuegeDbS (Satz x sx) r) Grundlagen der Programmierung 2 - 17 - Haskell-Implementierung zum Suchbaum Elementweises Einfügen : sortiert die Eingabe Vorsicht: Der entstehende Baum kann unbalanciert sein bewirkt Verschlechterung der Laufzeit von O(log (n)) auf O(n) Abhilfe: Balancieren des Baumes durch Rotationen Laufzeit des Baumaufbaus mit Balancieren: O(n ∗ log (n)) Grundlagen der Programmierung 2 - 18 - Haskell-Implementierung eines Suchbaumes (2) istinDbS (Satz x y) Suchbaumleer = False istinDbS (Satz x y) (Sblatt k _) = (x == k) istinDbS (Satz x y) (Sknoten bl k br) = if x < k then istinDbS (Satz x y) bl else if x == k then True else istinDbS (Satz x y) br erzeugeDbS [] = Suchbaumleer erzeugeDbS (x:xs) = einfuegeDbS (Satz x x) druckeDbS sb = blaetterSB Grundlagen der Programmierung 2 (erzeugeDbS xs) - 19 - Haskell-Implementierungen zum Suchbaum foldSB foldSB foldSB foldSB :: op op op (a -> b -> b) -> b -> Suchbaum a -> b a Suchbaumleer = a a (Sblatt _ x) = op x a a (Sknoten x _ y) = (foldSB op (foldSB op a y) x) blaetterSB = foldSB (:) [] Grundlagen der Programmierung 2 - 20 - Allgemeinere Bäume mit beliebiger Anzahl Töchter Die Programmierung ist analog zu einfachen Bäumen data Nbaum a = Nblatt a | Nknoten [Nbaum a] nbaumrand :: Nbaum a -> [a] nbaumrand (Nblatt x) = [x] nbaumrand (Nknoten xs) = concatMap nbaumrand xs Grundlagen der Programmierung 2 - 21 -