Was bisher geschah I Algorithmen I Datentypen I I Abstrakter Datentyp (Spezifikation): (funktionale) Signatur ΣF + Axiome Φ Konkreter Datentyp (Realisierung, Implementierung): ΣF -Algebra, die alle Axiome aus Φ erfüllt (d.h. Modell für Φ ist) I ADT Menge I lineare Datenstrukturen: I ADT Folge (mit und ohne Positionszugriff) Realisierungen: I I I I Arrays einfach verkettete Liste ADT Stack, ADT Queue rekursive Datenstrukturen (Listen) 113 Wiederholung: Bäume als Graphen Baum: azyklischer zusammenhängender Graph T = (V , E) hier außerdem I ausgezeichneter Knoten: Wurzel I gerichtete Kanten I Jeder Knoten hat höchstens einen Vorgänger. I Wurzel ist einziger Knoten ohne Vorgänger. I festgelegte Ordnung der Nachfolger (Kinder) jedes Knotens I Markierung der Knoten mit Werten, z.B. ∈ N Blatt: Knoten ohne Kinder (mit 0 Kindern) innerer Knoten: Knoten mit n > 0 Kindern 114 Baumstrukturen Anwendungen z.B. in I Gliederung eines Buches (Teile, Kapitel, Abschnitte) I Unternehmenstruktur in der Informatik z.B. in I Struktur von Termen, z.B. arithmetische Terme, Formeln I Programmstruktur (Anweisungen, Blöcke, Unterprogramme) I Verzeichnisstruktur (Betriebssystem) I Prozessstruktur (Betriebssystem) I XML-Dokumente 115 Bäume induktive Definition der Menge TreehEi aller Bäume mit Elementen (in den inneren Knoten) aus E: IA: der leere Baum Leaf ist ein Baum (Leaf ∈ TreehEi) IS: existiert ein n > 0 und sind t1 , . . . , tn Bäume (∀i ∈ {1, . . . , n} : ti ∈ Leaf ∈ TreehEi) und k ∈ E, dann ist auch Branch(k , (t1 , . . . , tn )) ein Baum (Branch(k , (t1 , . . . , tn )) ∈ TreehEi) graphische Darstellung (Tafel) rekursive Struktur: TreehEi = {Leaf} ∪ (E × (TreehEi)∗ ) 116 Spezialfälle Binärbäume (Kinderzahl aller inneren Knoten 2): induktive Definition der Menge BinTreehEi aller Binärbäume mit Elementen (in den inneren Knoten) aus E: IA: Leaf ∈ BinTreehEi) IS: left, right ∈ BinTreehEi ∧ key ∈ E → Branch(key, left, right) ∈ BinTree rekursive Struktur: BinTreehEi = {Leaf}∪(E×BinTreehEi×BinTreehEi) (Operationen key, left, right zum Zugriff auf diese Komponenten) Unärbäume (Kinderzahl aller inneren Knoten 1) Liste mit size = Länge 117 ADT Binärbaum mit Knotenmarkierungen vom Typ Element I Sorten: Bool, E, BT (kurz für BinTreehEi) I Signatur: Leaf : isEmpty : BT → Branch : E × BT × BT → left, right : BT → key : BT → t, f : I BT Bool BT BT E Bool Axiome, z.B. ∀k , j ∈ E mit k 6= j und l, r ∈ BT : isEmpty(Leaf) = t isEmpty(Branch(k , l, r )) = f left(Branch(k , l, r )) = l right(Branch(k , l, r )) = r contains(Branch(k , l, r ), k ) = t key(Branch(k , l, r )) = k contains(Branch(k , l, r ), j) = contains(l, j) ∨ contains(r , j) ... 118 Wiederholung: Länge einer Liste (Anzahl der Speicherpositionen) rekursiver Algorithmus: Algorithmus : size Eingabe : l ∈ ListhEi Ausgabe : n ∈ N wenn l = Nil dann n←0 sonst n ← 1 + size(tail(l)) 119 Größe eines Binärbaumes (Anzahl der Speicherpositionen) rekursiver Algorithmus: Algorithmus : size Eingabe : t ∈ BinTreehEi Ausgabe : n ∈ N wenn t = Leaf dann n←0 sonst n ← 1 + size(left(t)) + size(right(t)) 120 Weitere rekursive Funktionen auf Binärbäumen I Tiefe (Länge des längsten Wurzel-Blatt-Pfades) height(Leaf) = 0 height(Branch(k , l, r )) = 1 + max (height(l), height(r )) I Menge aller gespeicherten Werte (Inhalt) I Anzahl aller Vorkommen eines bestimmten Wertes I für Bäume über geordneten Mengen (E, ≤): minimales, maximales Element I Menge aller Teilbäume 121 Rekursive Durchquerungen von Binärbäumen Reihenfolge des Besuches aller Knoten eines Binärbaumes (z.B. zum Durchsuchen) Preorder preorder(Branch(k , l, r )) = k ◦preorder(l)◦preorder(r ) bei Ausgabe: Wurzel als Präfix Postorder postorder(Branch(k , l, r )) = postorder(l)◦postorder(r )◦k bei Ausgabe: Wurzel als Postfix Inorder inorder(Branch(k , l, r )) = inorder(l) ◦ k ◦ inorder(r ) bei Ausgabe: Wurzel als Infix ÜA: analoge Durchquerungen für allgemeine Bäume 122 Implementierung des ADT Menge durch Binärbäume Operationen: (Emptyset, isEmpty,) contains, add, remove Spezifikation der Funktion contains (auf ADT Menge): V: Eingabe Menge M ∈ MengehEi, x ∈E N: I I t, falls x ∈ M, f, sonst Realisierung des ADT Menge durch Binärbäume: Spezifikation der Funktion contains (auf Binärbäumen): V: Eingabe Binärbaum t ∈ BinTreehEi x ∈E N: I I t, falls t einen Knoten mit Wert x enthält (t also einen Teilbaum Branch(k , l, r ) mit k = x enthält) existiert f, sonst 123 contains auf Binärbäumen Bezeichnung: für t ∈ BinTreehEi ist die Menge aller in t gespeicherten Werte Inhalt(t) ⊆ 2E definiert durch I Inhalt(Leaf) = ∅ I Inhalt(Branch(k , l, r )) = {k } ∪ Inhalt(l) ∪ Inhalt(r ) Algorithmus : contains Eingabe : t ∈ BinTreehEi, e ∈ E Ausgabe : b ∈ {t, f} mit b = t gdw. e ∈ Inhalt(t) wenn t = Leaf dann b ← f sonst wenn e = key(t) dann b ← t sonst b ← contains(left(t), e) ∨ contains(right(t), e) oder kürzer: contains(t, e) ← t 6= Leaf ∧ (e = key(t) ∨ contains(left(t)) ∨ contains(right(t))) 124 add auf Binärbäumen ein möglicher Algorithmus (Einfügen links unten): Algorithmus : add Eingabe : t ∈ BinTreehEi, e ∈ E Ausgabe : t 0 ∈ BinTreehEi mit Inhalt(t 0 ) = Inhalt(t) ∪ {e} wenn l = Leaf dann t 0 ← Branch(e, Leaf, Leaf) sonst t 0 ← Branch(key(t), add(left(t), e), right(t)) Laufzeit für Baum t mit n Knoten: O(height(t)) best case O(log n), worst case O(n) remove? für Teilbäume Branch(e, l, r ) mit l = Leaf oder r = Leaf einfach, für andere Knoten schwieriger (später) 125 Binäre Suchbäume Der Binärbaum t hat die Suchbaum-Eigenschaft gdw. für jeden Teilbaum Branch(x, l, r ) von t gilt: I für jeden Schlüsselwert y eines inneren Knotens von l gilt y <x I für jeden Schlüsselwert y eines inneren Knotens von r gilt x <y Ergebnis der Inorder-Durchquerung jedes binären Suchbaumes ist eine aufsteigend sortierte Folge Laufzeit für sortierte Ausgabe aller im Suchbaum mit n Knoten enthaltenen Schlüssel: O(n) 126 Binäre Suchbäume: contains Spezifikation der Funktion contains (auf binärem Suchbaum): V: Eingabe t ∈ BinTreehEi mit Suchbaum-Eigenschaft, e ∈ E N: Ausgabe t, falls t einen Teilbaum Branch(e, l, r ) enthält f, sonst Idee (rekursiv): IA: Der leere Baum Leaf enthält keinen Schlüssel. IS: Baum Branch(k , l, r ) enthält e falls I I I e = k oder e < k und linkes Kind enthält e oder e > k und rechtes Kind enthält e 127 Binäre Suchbäume: contains Algorithmus : contains Eingabe : t ∈ BinTreehEi mit Suchbaum-Eigenschaft, e ∈ E Ausgabe : b ∈ {t, f} mit b = t gdw. e ∈ Inhalt(t) wenn l = Leaf dann b ← f sonst wenn e = key(t) dann b ← t sonst wenn e < key(t) dann b ← contains(left(t), e) sonst b ← contains(right(t), e) 128 Laufzeit der Suche Laufzeit von contains(t, e) hängt von der Mächtigkeit N der Menge (Anzahl der inneren mitKnoten in t) ab: I falls t einen Teilbaum Branch(e, l, r ) enthält: Länge des Pfades von Wurzel zu Teilbaum I falls t keinen Teilbaum Branch(e, l, r ) enthält: Länge eines längsten Wurzel-Blatt-Pfades (Höhe des Baumes t) höchstens Höhe des Baumes t (log(n) ≤ height(t) ≤ n) Suche im Suchbaum t in O(height(t)) (i.A. schneller als lineare Suche in einer Folge) 129 Extremwerte in binären Suchbäumen Spezifikation Minimum: V: Eingabe binärer Suchbaum t ∈ BinTreehEi über total geordneter Menge E N: Ausgabe m: minimaler in t vorkommender Schlüsselwert (undefiniert für leeren Baum) Idee: in jedem binären Suchbaum steht der minimale Schlüsselwert im äußeren linken Knoten (Minimum lässt sich ohne Schlüsselvergleich bestimmen.) Algorithmus : minimum Eingabe : t ∈ BinTreehEi mit Suchbaum-Eigenschaft Ausgabe : m ∈ E mit ∀e ∈ Inhalt(t) : m ≤ e wenn t = Leaf dann m ← undefiniert sonst wenn t = Branch(k , Leaf, r ) dann m ← k sonst m ← minimum(left(t)) Laufzeit höchstens Tiefe des Baumes t (log(n) ≤ height(t) ≤ n) Maximum analog (äußerer rechter Knoten) 130 Einfügen in binäre Suchbäume Spezifikation add: V: Eingabe t ∈ BinTreehEi mit Suchbaum-Eigenschaft, e ∈ E N: Ausgabe t 0 ∈ BinTreehEi mit Suchbaum-Eigenschaft und Inhalt(t 0 ) = Inhalt(t) ∪ {e} Idee (rekursiv): IA: Einfügen in leeren Baum durch Erzeugung eines Knotens IS: in jedem Teilbaum: I I I schon vorhandene Schlüssel nicht einfügen (Menge) Schlüssel < Wurzelschlüssel in linkes Kind einfügen Schlüssel > Wurzelschlüssel in rechtes Kind einfügen 131 Einfügen in binäre Suchbäume Algorithmus : add Eingabe : t ∈ BinTreehEi mit Suchbaum-Eigenschaft, e ∈ E Ausgabe : t 0 ∈ BinTreehEi mit Suchbaum-Eigenschaft und Inhalt(t 0 ) = Inhalt(t) ∪ {e} wenn t = Leaf dann t 0 ← Branch(e, Leaf, Leaf) sonst wenn e = key(t) dann t 0 ← t sonst wenn e < key(t) dann t 0 ← add(left(t), e) sonst t 0 ← add(right(t), e) 132 Iteriertes Einfügen Beim Einfügen der Elemente der Menge {2, 3, 5, 7} in verschiedenen Reihenfolgen in leeren Baum entstehen i.A. veschiedene Bäume Beispiel (Tafel): I 3,7,5,2 I 5,3,7,2 I 2,3,5,7 Daraus lässt sich ein Sortierverfahren ableiten. Sortieren durch Einfügen in binären Suchbaum: 1. schrittweises Einfügen aller Elemente einer Menge in einen zu Beginn leeren binären Suchbaum 2. Sortierte Ausgabe durch Inorder-Durchquerung des so entstandenen binären Suchbaumes 133 Laufzeit Einfügen Idee: Schlüsselwert e wird dort in t eingefügt, wo ihn der Suchalgorithmus (contains) in t finden würde. I falls e schon vorhanden: kein Einfügen I falls e noch nicht vorhanden (Blatt): Ersetzen des Blattes durch neuen Knoten Branch(e, Leaf, Leaf) also dieselbe Laufzeit wie contains: höchstens Höhe des Baumes t (log(n) ≤ height(t) ≤ n) Einfügen in einen binären Suchbaum mit n Knoten in O(height(t)) Laufzeit des abgeleiteten Sortierverfahrens: O(n height(t)) 134 Entfernen aus binären Suchbäumen Spezifikation remove: V: Eingabe t ∈ BinTreehEi mit Suchbaum-Eigenschaft, e ∈ E N: Ausgabe t 0 ∈ BinTreehEi mit Suchbaum-Eigenschaft und Inhalt(t 0 ) = Inhalt(t) \ {e} mögliche Fälle: 1. e kommt nicht in t vor: Ergebnis t 0 = t 2. e ist Schlüsselwert eines Teilbaumes s in t mit zwei leeren Kindern: Löschen des Knotens s (Ersetzen durch Leaf) 3. e ist Schlüsselwert eines Knotens s in t mit einem leeren Kind: Ersetzen des Knotens s durch sein einziges nichtleeres Kind 4. e ist Schlüsselwert eines Knotens s in t mit zwei nichtleeren Kindern: Tausch der Schlüsselwerte in s und dem linken äußeren Knoten r des rechten Kindes von s (Minimum des rechten Kindes) Löschen des Knotens r (rekursiv) Warum hat der so entstandene Baum die Suchbaumeigenschaft? Laufzeit für Löschen aus binärem Suchbaum t: O(height(t)) 135 Binäre Suchbäume: remove Algorithmus : remove Eingabe : t ∈ BinTreehEi mit Suchbaum-Eigenschaft, e ∈ E Ausgabe : t 0 ∈ BinTreehEi mit Inhalt(t 0 ) = Inhalt(t) \ {e} wenn t = Leaf dann t 0 ← Leaf sonst wenn e = key(t) dann wenn left(t) = Leaf dann t 0 ← remove(right(t), e) sonst wenn right(t) = Leaf dann t 0 ← remove(left(t), e) sonst t 0 ← Branch(minimum(right(t)), l, remove(right(t), minimum(right(t)))) sonst wenn e < key(t) dann t 0 ← Branch(key(t), remove(left(t), e), right(t)) sonst t 0 ← Branch(key(t), left(t), remove(right(t), e)) 136 Laufzeiten in binären Suchbäumen I contains I add I remove in O(height(t)) Laufzeiten in Abhängigkeit von der Knotenzahl n = size(t): Extremfälle: I für Pfade (entartete Bäume) height(t) = size(t) Laufzeit der Operationen O(n) I für balancierte Bäume height(t) = log size(t) Laufzeit der Operationen O(log(n)) 137 Anwendung von Suchbäumen (Balancierte) binäre Suchbäume sind geeignet zum I Speichern, I schnellen Wiederfinden I Entfernen, I sortierten Augeben von Daten anhand ihnen zugeordneten Schlüsselwerten (Schlüsselwerte müssen total geordnete Menge bilden) I Realisierung des ADT Menge für linear geordnete Mengen I Realisierung von Wörterbüchern (= Mengen von Paaren (Schlüssel, Wert) mit eindeutigem Schlüssel aus einer linear geordneten Menge, z.B. ⊆ )), (= partielle Funktionen: Schlüsselbereich → Wertebereich): Operationen: contains, add, remove, jeweils von Paaren (Schlüssel, Daten) z.B. Telefonbucheinträge über Namen (alphabetisch geordnet), Studenten über Studentennummern N 138 Balancierte binäre Suchbäume Laufzeit für Suche, Einfügen, Löschen in Baum t mit n Knoten: O(height(t)) Ziel: Laufzeit für Suche, Einfügen, Löschen O(log n) Idee: balancierte Suchbäume, in denen die Tiefe jedes Blattes (etwa) gleich ist (also O(log n)) 139 Vollständig balancierte Bäume Binärer Suchbaum t heißt vollständig balanciert, wenn in jedem Knoten Branch(x, l, r ) in t gilt: |size(l) − size(r )| ≤ 1 Tiefe jedes Knotens ≤ blog(n)c + 1 Beispiel (Tafel) 140 Operationen in vollständig balancierten Bäumen Emptyset O(1) isEmpty O(1) contains O(log(n)) add, remove in je zwei Schritten: 1. Einfüge- und Löschoperation für balancierte binäre Suchbäume: O(log(n)) 2. Rebalancieren, d.h. Wiederherstellen der vollständigen Balance: i.A. aufwendig 141 Balancierte Suchbäume Problem: (Zeit-)Aufwand zum Herstellen der vollständigen Balanche nach Einfügen und Löschen zu hoch (> O(log n)) Verschiedene Lösungsansätze zur Realisierung der Operationen contains, add, remove in Laufzeit O(log n): I Binärbäume mit schwächerer (aber ausreichender) Balance-Bedingung, die nach Einfügen und Löschen in O(log n) Schritten wiederherstellbar ist (daher Erhalt der Laufzeit der Operationen), z.B. I I I AVL-Bäume Rot-Schwarz-Bäume Mehrweg-Suchbäume: vollständig balancierte Bäume mit verschiedenen Knotengraden, z.B. I I 2-3-Bäume (innere Knoten mit 2 oder 3 Kindern) B-Bäume (innere Knoten mit zwischen n/2 und n Kindern) 142