Datenstruktur Baum Grundlagen der Programmierung 4 Binäre, geordnete Bäume in Haskell: Haskell: Bäume Prof. Dr Manfred Schmidt-Schauß data Binbaum a = Bblatt a | Bknoten (Binbaum a) (Binbaum a) • • • Daten (Markierungen) sind an den Blättern des Baumes Jeder (innere) Knoten hat genau zwei Tochterknoten es gibt linken und rechten Tochterknoten (geordnet) Sommersemester 2013 1 Grundlagen der Programmierung 2 Bäume) Datenstruktur Baum; Beispiel Datenstruktur Baum; Beispiel Der folgende binäre Baum Bknoten (Bknoten (Bblatt 1) (Bknoten (Bblatt 3) (Bblatt 4))) (Bknoten (Bblatt 7) (Bblatt 8)) · ·t 1 3 · 7 – 2/16 – · Als Syntaxbaum: 8 Bknoten 4 Bknoten hat eine Darstellung als x 1 w BBlatt – 3/16 – w Bknoten 3 Grundlagen der Programmierung 2 Bäume) Bknoten ' BBlatt Bknoten (Bknoten (Bblatt 1) (Bknoten (Bblatt 3) (Bblatt 4))) (Bknoten (Bblatt 7) (Bblatt 8)) ' r Grundlagen der Programmierung 2 Bäume) BBlatt & BBlatt & BBlatt 7 8 4 – 4/16 – Einige Verarbeitungsfunktionen Einige Verarbeitungsfunktionen (2) Rand: Liste der Markierungen der Blätter: Größe des Baumes: b_rand (Bblatt x) = [x] b_rand (Bknoten bl br) = (b_rand bl) ++ (b_rand br) testet, ob eine Markierung existiert: b_in x (Bblatt y) = (x == y) b_in x (Bknoten bl br) = b_in x bl 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) 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äume) b_size (Bblatt x) = b_size (Bknoten bl br) = Tiefe des Baumes: (b_map f br) – 5/16 – Einige Verarbeitungsfunktionen (3) b_depth (Bblatt x) = 0 b_depth (Bknoten bl br) = 1 + (max (b_depth bl) (b_depth br)) Grundlagen der Programmierung 2 Bäume) – 6/16 – Auswertung mit foldbt schnelles fold über binäre Bäume: Werte foldbt (+) 0 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) <<<1,2>,3>,<4 ,5>> aus: (+) 0 <<<1,2>,3>,<4,5>> (+) (foldbt (+) 0 <4,5>) <<1,2>,3> (+) (foldbt (+) (foldbt (+) 0 <5>) <4>) <<1,2>,3> (+) (foldbt (+) (5 + 0) <4>) <<1,2>,3> (+) (4 + (5 +0)) <<1,2>,3> (+) (foldbt (+) (4 + (5 + 0)) <3>) <1,2> (+) (3 + (4 + (5 + 0))) <1,2> (+) (foldbt (+) (3 + (4 + (5 + 0))) <2>) <1>) (+) (2 + (3 + (4 + (5 + 0)))) <1> + (3 + (4 + (5 + 0)))) (Der Baum ist vereinfacht als Tupel von Tupeln mit dargestellt) tatsächlicher Aufruf nach Laden des Programmfiles effizientere Version von b rand und b sum b_rand_eff = foldbt (:) [] b_baum_sum’ = foldbt’ (+) 0 Grundlagen der Programmierung 2 Bäume) foldbt foldbt foldbt foldbt foldbt foldbt foldbt foldbt foldbt 1 + (2 foldbts (+) 0 (Bk (Bk (Bk (Bb 1) (Bb 2)) (Bb 3)) (Bk (Bb 4) (Bb 5))) – 7/16 – Grundlagen der Programmierung 2 Bäume) – 8/16 – Bemerkungen zu foldbt Suchbaum Effizienzsteigerung beim Zugriff auf Elemente einer Liste: Sei tr binärer Baum mit Randliste [a1 , . . . , an ] sortierte Liste als Baum (foldbt f a tr) entspricht (f a_1 (f a_2 (... (f a_n a) ...s))). Jeder Knoten enthält als Markierung den größten Schlüssel seines linken Teilbaumes D.h. foldbt entspricht foldr Laufzeit des Such-Zugriffs: logarithmisch in der Anzahl der Elemente Grundlagen der Programmierung 2 Bäume) – 9/16 – Suchbaum, Beispiel Grundlagen der Programmierung 2 Bäume) – 10/16 – Haskell-Implementierung: Suchbaum 5 1u 1 3 Grundlagen der Programmierung 2 Bäume) 3 7 data Satz a = Satz Integer a data Suchbaum a = Sblatt Integer a | Sknoten (Suchbaum a) Integer (Suchbaum a) | Suchbaumleer 7 11 Verwendung: Suchbaum (Satz a) 5 – 11/16 – Grundlagen der Programmierung 2 Bäume) – 12/16 – Haskell-Implementierung: Suchbaum Haskell-Implementierung zum 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) Elementweises Einfügen : sortiert die Eingabe Vorsicht: Der naiv erzeugte Baum kann unbalanciert sein! das bewirkt Verschlechterung der Laufzeit von O(log(n)) auf O(n) Abhilfe: Balancieren des Baumes durch Rotationen zusammen mit dem Einfügen Laufzeit des Baumaufbaus mit Balancieren: Grundlagen der Programmierung 2 Bäume) – 13/16 – Haskell-Library zu Suchbäumen – 14/16 – mit beliebiger Anzahl Töchter Die Programmierung ist analog zu einfachen Bäumen implementiert balancierte Suchbäume in Haskell: (Schlüssel,Wert) - Paare, so dass pro Schlüssel nur ein Paar vorkommt. data Nbaum a = Nblatt a | Nknoten [Nbaum a] erzeugt Suchbaum mit einem (Schlüssel, Wert)-Paar fügt ein Eement ein. löscht ein Element zu gegebenem Schlüssel. findet ein Element zu gegebenem Schlüssel ändert Wert zu gegebenem Schlüssel Grundlagen der Programmierung 2 Bäume) Grundlagen der Programmierung 2 Bäume) Allgemeinere Bäume Data.Map Funktionen: singleton insert delete lookup adjust O(n ∗ log(n)) – 15/16 – nbaumrand :: Nbaum a -> [a] nbaumrand (Nblatt x) = [x] nbaumrand (Nknoten xs) = concatMap nbaumrand xs Grundlagen der Programmierung 2 Bäume) – 16/16 –