Informatik II - 168 Kapitel 8 Suchverfahren 8.1 Laufzeitanalyse von Algorithmen und O-Notation Die Effizienz ist ein wichtiges Kriterium zum Vergleich verschiedener Algorithmen zur Lösung ein und desselben Problems. Sie wird bestimmt durch den benötigten Aufwand des Algorithmus (seine Komplexität) in Abhängigkeit von einer speziellen Eingabesituation. Wesentliche Effizienzkriterien sind • die Laufzeit des Algorithmus • der benötigte Speicherplatz Häufig: ‘trade-off’ bei der Optimierung eines dieser beiden Kriterien dahingehend, daß das andere Kriterium verschlechtert wird. In der Regel ist das wichtigere Kriterium die Laufzeit. Wir werden uns daher im folgenden auf die Laufzeitanalyse beschränken. Laufzeitanalyse 1. Ansatz: Direktes Messen der Laufzeit, z.B. in Millisekunden. ➙ abhängig von vielen Parametern, wie Rechnerkonfiguration, Rechnerlast, Compiler, Betriebssystem, Programmiertricks, u.a., und damit sehr unzuverlässig und ungenau. 2. Ansatz: Zählen der benötigten Elementaroperationen des Algorithmus in Abhängigkeit von der Größe der Eingabe. ➙ das algorithmische Verhalten wird als Funktion der benötigten Elementaroperationen dargestellt. Die Charakterisierung dieser elementaren Operationen ist abhängig von der jeweiligen Problemstellung und dem zugrundeliegenden Algorithmus. Beispiele für Elementaroperationen sind Zuweisungen, Vergleiche, arithmetische Operationen, Zeigerdereferenzierungen oder Arrayzugriffe. ➙ das Maß für die Größe der Eingabe ist abhängig von der Problemstellung, z.B. Problem Größe der Eingabe Suche eines Elementes in einer Liste Anzahl der Elemente Multiplikation zweier Matrizen Dimension der Matrizen Sortierung einer Liste von Zahlen Anzahl der Zahlen Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 169 Betrachten wir nochmals die Laufzeit des binären Suchens. Für die Anzahl der Vergleiche im schlechtesten Fall hatten wir ermittelt: A worst ( n ) = log 2 ( n ) + 1 . Durch Weglassen multiplikativer und additiver Konstanten kommen wir von der Anzahl der Schlüsselvergleiche zur Laufzeitfunktion. Damit erhält man eine von der Programmumgebung und anderen äußeren Einflußgrößen (z.B.: dem Zeitbedarf einer Vergleichsoperation, der Effizienz der Programmierung, ...) unabhängige Charakterisierung der (asymptotischen) Komplexität des Algorithmus. ➙ O-Notation verwenden Definition: O-Notation + + Seien f: ( N → ℜ ) und g: ( N → ℜ ) . f = O(g) ⇔ ∃ n 0 ∈ N , c ∈ ℜ + mit ∀ n ≥ n 0 ist f(n) ≤ c ⋅ g(n) . Man sagt auch: f wächst höchstens so schnell wie g . Anmerkung: Da O(g) genau genommen eine (unendliche) Menge von Funktionen beschreibt, ist es genauer, zu sagen: f ∈ O(g) . In der Literatur hat sich jedoch das ‘=’ eingebürgert. Wichtig ist es, darauf zu achten, daß insbesondere die Kommutativität des ‘=’Operators hier nicht gilt. Mit dieser Notation gilt: T worst(n) = O(Aworst(n)) . Im Beispiel des binären Suchens also: T worst(n) = O( log 2 ( n ) + 1) = O ( logn ) . Funktionen, die bei der Laufzeitanalyse von Algorithmen realistischerweise auftauchen, sind meist monoton wachsend und von 0 verschieden. Zur Überprüfung obiger Eigenschaft betrachtet man dabei den Quotienten f(n) ⁄ g(n) . Nach Definition gilt für f = O(g) : f(n) ⁄ g(n) ≤ c für n ≥ n 0 . f(n) Man betrachte den Grenzwert: lim ---------- . Existiert dieser (d.h. er ist < ∞), so ist f = O(g) . g n → ∞ (n) Rechnen mit der O-Notation: • Elimination von Konstanten: 2 ⋅ n = O(n) , n ⁄ 2 + 1 = O(n) • Bilden oberer Schranken: 2 ⋅ n = O(n ) , 3 = O(log n) 2 Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 170 Wichtige Klassen von Funktionen: Sprechweise Typische Algorithmen O(1) konstant O(log n) logarithmisch Suchen auf einer Menge O(n) linear Bearbeiten jedes Elementes einer Menge O(n ⋅ log n) Gute Sortierverfahren, z.B. Heapsort 2 O(n ⋅ log n) ... 2 O(n ) k O(n ), k ≥ 2 quadratisch primitive Sortierverfahren polynomiell ... n O(2 ) exponentiell Backtracking-Algorithmen Nach diesem Exkurs in die O-Notation wollen wir uns nochmals Bäumen zuwenden, diesmal nicht beschränkt auf binäre Bäume. Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 171 8.2 Baumstrukturen Ein Baum ist eine endliche Menge T von Elementen, Knoten genannt, mit: (1) Es gibt einen ausgezeichneten Knoten w(T) , die Wurzel von T (2) Die restlichen Knoten sind in m ≥ 0 disjunkte Mengen T1, …, Tm zerlegt, die ihrerseits Bäume sind. T1, …, Tm heißen Teilbäume der Wurzel w(T) . (rekursive Definition) T w(T) T2 T1 … Tm Eine lineare Liste ist ein (entarteter) Baum, in dem jeder Knoten höchstens einen Teilbaum besitzt. Bäume erlauben es, hierarchische Beziehungen zwischen Objekten darzustellen. Derartige Hierarchien treten vielfach in der realen Welt auf, z.B.: • Die Strukturierung eines Unternehmens in Bereiche, Abteilungen, Gruppen und Angestellte Unternehmen Bereich 3 Abteilung 1 Gruppe 1 Müller Meier Kuhn Schmidt • Die Gliederung eines Buches in Kapitel, Abschnitte, Unterabschnitte • Die Aufteilung Deutschlands in Bundesländer, Bezirke, Kreise, Gemeinden • Darstellung (geklammerter) arithmetischer Ausdrücke als binäre Bäume (d.h. m ≤ 2 für alle Knoten des Baumes). Diese Bäume heißen Operatorbäume. Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 172 Operatorbaum für: a - b * (c / d + e / f) a * + b / c / d e f Der Grad eines Knotens x, deg(x), ist gleich der Anzahl der Teilbäume von x. Gilt deg(x) = 0, so nennt man x ein Blatt. Der Grad des Baumes ist der maximale Grad aller seiner Knoten. Jeder Knoten x ≠ w(T) hat einen eindeutigen Vorgänger v(x), auch Vater von x genannt. s(x) bezeichnet die Menge aller Söhne (auch Kinder oder Nachfolger von x genannt) und b(x) die Menge aller Brüder von x (auch Geschwister von x genannt). Alle Brüder in b(x) haben denselben Vater wie x. Ein Pfad in einem Baum ist eine Folge von Knoten p1, …, pn mit: pi = v(pi+1), i = 1, …, n-1. Die Länge des Pfades ist n. 1 für x = w(T) Der Level eines Knotens x, lev(x) ist: lev(x) = . für x ≠ w(T) lev(v(x)) + 1 Damit ist lev(x) gleich der Anzahl der Knoten auf dem Pfad von der Wurzel zum Knoten x. Die Höhe eines Baumes ist gleich dem maximalen Level seiner Knoten. Dies entspricht der Länge des längsten von der Wurzel ausgehenden Pfades innerhalb des Baumes. Ein Wald ist eine geordnete Menge von n ≥ 0 disjunkten Bäumen. Entfernt man die Wurzel eines Baumes, so erhält man einen Wald. Ein binärer Baum ist eine endliche Menge B von Knoten, die • entweder leer ist • oder aus einer Wurzel und zwei disjunkten binären Bäumen besteht, dem linken und dem rechten Teilbaum der Wurzel. A Damit sind: B und A zwei verschiedene binäre Bäume, aber als Bäume gleich. B Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 173 Suchverfahren Gegeben sei eine Menge von Objekten, die durch (eindeutige) Schlüssel charakterisiert sind. Aufgabe von Suchverfahren ist die Suche bestimmter Objekte anhand ihres Schlüssels. Bisher: zwei Methoden der Speicherung solcher Objekte • sequentielle Speicherung ➙ sortiertes Array (binäre Suche) • verkettete Speicherung ➙ lineare Liste Zeitaufwand im schlechtesten Fall für eine Menge von n Elementen; Operation sequentiell gespeichert (sortiertes ARRAY) verkettet gespeichert (lineare Liste) Suche Objekt mit gegebenem Schlüssel O(log n) O(n) Einfügen an bekannter Stelle O(n) O(1) Entfernen an bekannter Stelle O(n) O(1) Im folgenden werden wir Verfahren behandeln, die sowohl die Suche als auch das Einfügen und Entfernen “effizient” unterstützen (bessere Laufzeitkomplexität als O(n)). 8.3 Binäre Suchbäume Ziel: Die Operationen Suchen, Einfügen und Entfernen sollen alle in O(log n) Zeit durchgeführt werden. Ansatz: Organisation der Objektmenge als Knoten eines binären Baumes. Definition: Ein binärer Baum heißt binärer Suchbaum, wenn für jeden seiner Knoten die Suchbaumeigenschaft gilt, d.h. alle Schlüssel im linken Teilbaum sind kleiner, alle Schlüssel im rechten Teilbaum sind größer als der Schlüssel im Knoten: k <k >k linker Teilbaum rechter Teilbaum Tl Tr Anmerkungen: • Zur Organisation einer gegebenen Menge von n Schlüsseln gibt es eine große Anzahl unterschiedlicher binärer Suchbäume. • Der inorder-Durchlauf eines binären Suchbaumes generiert die (aufsteigend) sortierte Folge der gespeicherten Schlüssel. Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 174 Beispiele: Zwei verschiedene binäre Suchbäume über den Monatsnamen: DEZ JAN AUG FEB NOV MÄR APR APR MAI AUG JUN DEZ JUL SEP JUL OKT FEB OKT JUN SEP MAI JAN MÄR NOV Der Entscheidungsbaum zur binären Suche ist ein binärer Suchbaum: 17 8 23 5 11 3 0 7 1 2 9 3 4 19 13 5 6 18 7 8 27 21 9 24 30 10 11 12 13 14 31 15 16 8.3.1 Allgemeine binäre Suchbäume class BinaryNode { int key; BinaryNode left; BinaryNode right; BinaryNode(int k) { key=k; left = null; right = null;} } public class BinarySearchTree { BinaryNode root; public BinarySearchTree () { root = null; } // ... Implementierung der Methoden insert, find, delete,... } Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 175 Die Methode insert (int x) der Klasse BinarySearchTree fügt einen gegebenen Schlüssel x ein, falls dieser noch nicht im Baum enthalten ist: // Methoden der Klasse BinarySearchTree public void insert (int x) throws Exception { root = insert (x, root);} protected BinaryNode insert (int x, BinaryNode t) throws Exception { if ( t == null) t = new BinaryNode (x); else if (x < t.key) t.left = insert (x, t.left); else if (x > t.key) t.right = insert (x, t.right); else throw new Exception (“Inserting twice”); return t; } Die überladene interne Methode insert (x, t) liefert eine Referenz auf die Wurzel des Teilbaums zurück, in den x eingefügt wurde. Die folgende Methode find (x) sucht in einem binären Suchbaum den Schlüssel x und liefert diesen zurück, falls er gefunden wurde. Andernfalls wird eine Ausnahmebehandlung durchgeführt. // Methoden der Klasse BinarySearchTree public int find (int x) throws Exception { return find (x, root).key;} protected BinaryNode find (int x, BinaryNode t) throws Exception { while ( t != null) {if ( x < t.key) t = t.left; else if ( x > t.key) t = t.right; else return t; // Match } throw new Exception (“key not found”); } Das Entfernen eines Schlüssels ist etwas komplexer, da auch Schlüssel in inneren Knoten des Baumes betroffen sein können und die Suchbaumstruktur aufrecht erhalten werden muß. Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 176 Hierzu zunächst ein Beispiel: 7 3 19 7 14 3 14 entfernen 33 19 33 19 3 7 entfernen 33 19 52 3 52 33 26 52 26 26 52 19 26 Allgemein treten zwei grundsätzlich verschiedene Situationen auf: Fall 1: der Knoten q mit dem zu entfernenden Schlüssel besitzt höchstens einen Sohn (Blatt oder Halbblatt) Fall 2: der Knoten q mit dem zu entfernenden Schlüssel besitzt zwei Söhne (innerer Knoten) Fall 1: a) der Knoten q besitzt keinen Sohn p b) der Knoten q besitzt einen linken Sohn (rechts➙symmetrisch) p p q p q null null null null p.right = q.left; p.right = null; Fall 2: p p q s Programmcode: s. Methode‘deleteMin’ T1 T1 r r s T3 T2 null T3 T2 Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 177 Die folgende Methode delete(x) entfernt einen Schüssel x aus einem binären Suchbaum. Hierbei wird eine Hilfsmethode findMin verwendet, die den Knoten des kleinsten Schlüssels des rechten Teilbaumes (eines inneren Knotens) bestimmt und eine weitere Hilfsmethode deleteMin, die diesen Knoten entfernt: // Methoden der Klasse BinarySearchTree public void delete(int x) throws Exception { root = delete (x, root);} protected BinaryNode delete (int x, BinaryNode t) throws Exception { if (t == null) throw new Exception (“x does not exist (delete)”); if (x < t.key) t.left = delete (x, t.left); else if (x > t.key) t.right = delete (x, t.right); else if (t.left != null && t.right != null)// x is in inner node t { t.key = findMin (t.right).key; t.right = deleteMin (t.right); } else // x is in leaf or in semi-leaf node, reroot t t = (t.left != null) ? t.left : t.right; return t; } protected BinaryNode findMin (BinaryNode t) throws Exception { if ( t == null) throw new Exception (“key not found (findMin)”); while ( t.left != null) t = t.left; return t; } protected BinaryNode deleteMin (BinaryNode t) throws Exception { if (t == null) throw new Exception (“key not found (deleteMin)”); if (t.left != null) t.left = deleteMin (t.left); else t = t.right; return t; } Zum Verständnis: Der Fall des Entfernens aus einem inneren Knoten (t.right != null && t.left != null) wird auf eine ‘Blatt-’ (t.right == null && t.left == null) oder eine ‘HalbblattSituation’ (t.right != null || t.left != null) zurückgeführt, indem der zu löschende Schlüssel durch den kleinsten der größeren Schlüssel ersetzt wird. Dieser Schlüssel befindet sich stets in einem Blatt oder einem Halbblatt, das daraufhin aus dem Baum entfernt wird. Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 178 Laufzeitanalyse der Algorithmen insert, find und delete Alle drei Methoden sind auf einen einzigen, bei der Wurzel beginnenden Pfad des Suchbaumes beschränkt. ➙ der maximale Aufwand ist damit O(h) , wobei h die Höhe des Baumes ist. Für die Höhe binärer Bäume mit n Knoten gilt: • Die maximale Höhe eines binären Baumes mit n Knoten ist n. (➙ Lineare Liste) • Seine minimale Höhe ist log2(n+1) Begründung: Für eine gegebene Anzahl n von Knoten haben die sogenannten vollständig ausgeglichenen binären Bäume minimale Höhe. In einem vollständig ausgeglichenen binären Baum müssen alle Levels bis auf das unterste vollständig besetzt sein. Die maximale Anzahl n von Knoten in einem vollständig h–1 ausgeglichenen binären Baum der Höhe h ist: i h n = ∑2 = 2 –1 i=0 ➙ h min = log2(n + 1) . Bemerkung: Es gilt: log 2(n + 1) = log 2(n) + 1 ∀n ∈ IN . Eine aufwendige Durchschnittsanalyse ergibt unter den beiden Annahmen: • der Baum ist nur durch Einfügungen entstanden und • alle möglichen Permutationen der Eingabereihenfolge sind gleichwahrscheinlich einen mittleren Wert h ∅ = 2 ⋅ ln 2 ⋅ log n ≈ 1,386 ⋅ log n . Der durchschnittliche Zeitbedarf für Suchen, Einfügen und Entfernen ist damit O(log n) . Kritikpunkt am naiven Algorithmus zum Aufbau binärer Suchbäume und damit an der Klasse so erzeugter binärer Suchbäume: ➙ im worst-case ist der Aufwand aller drei Operationen O(n) . 8.3.2 Vollständig ausgeglichene binäre Suchbäume Die minimale Höhe log 2(n + 1) unter allen binären Suchbäumen besitzen die vollständig ausgeglichenen binären Suchbäume, d.h. binäre Suchbäume, bei denen alle Levels bis auf das unterste vollständig besetzt sind. ➙ optimaler Zeitaufwand für Suchoperationen Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 179 Vollständig ausgeglichene binäre Bäume für verschiedene Knotenzahlen n: n=1 n=3 n=2 n=4 n=6 n=5 n=7 Level 1 n = 13 2 Höhe 4 3 4 Beispiel: Einfügen von Schlüssel ‘B’ in einen bestehenden Baum E D C A G D F B A F C E G B Problem: Der Baum muß beim Einfügen von B vollständig reorganisiert werden. ➙ Einfügezeit im schlechtesten Fall: O(n) . ➙ Auswahl einer Kompromißlösung mit den Eigenschaften: • die Höhe des Baumes ist im schlechtesten Fall O(log n) . • Reorganisationen bleiben auf den Suchpfad zum einzufügenden bzw.. zu entfernenden Schlüssel beschränkt und sind damit im schlechtesten Fall in O(log n) Zeit ausführbar. Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 180 Definition: Eine Klasse von Suchbäumen heißt balanciert, falls: • h max = O(log n) • die Operationen Suchen, Einfügen und Entfernen sind auf einen Pfad von der Wurzel zu einem Blatt beschränkt und benötigen damit im schlechtesten Fall O(log n) Zeit. Klasse der Suchbäume Klasse balancierter Suchbäume Klasse der vollständig ausgeglichenen Suchbäume (Höhe = log2 (n+1)) 8.3.3 AVL-Bäume (Adelson-Velskij und Landis (1962)) AVL-Bäume sind ein Beispiel für eine Klasse balancierter Suchbäume. Definition: Ein binärer Suchbaum heißt AVL-Baum, falls für die beiden Teilbäume Tr und Tl der Wurzel gilt: • h(T r) – h(T l) ≤ 1 • Tr und Tl sind ihrerseits AVL-Bäume. (rekursive Definition) Der Wert h(T r) – h(T l) wird als Balancefaktor (BF) eines Knotens bezeichnet. Er kann in einem AVL-Baum nur die Werte -1, 0 oder 1 (dargestellt durch -, = und +) annehmen. Mögliche Strukturverletzungen durch Einfügungen bzw. Entfernungen von Schlüsseln erfordern Rebalancierungsoperationen. Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 181 Beispiel für einen AVL-Baum: - L N I + E B C = P = G = O K - = A = M J + = = R = = = F Behauptung: Die minimale Höhe h min(n) eines AVL-Baumes mit n Schlüsseln ist log 2(n + 1) . Dies folgt aus der Tatsache, daß ein AVL-Baum minimaler Höhe einem vollständig ausgeglichenen binären Suchbaum entspricht. Behauptung: Die maximale Höhe h max(n) eines AVL-Baumes mit n Schlüsseln ist O(log n) . Beweis: Die maximale Höhe wird realisiert von sogenannten minimalen AVL-Bäumen. Dies sind AVL-Bäume, die für eine gegebene Höhe h die minimale Anzahl von Schlüsseln abspeichern. Minimale AVL-Bäume haben bis auf Symmetrie die folgende Gestalt: nh-1 nh-2 Sei nh die minimale Anzahl von Schlüsseln in einem AVL-Baum der Höhe h. Dann gilt: n h = n h – 1 + n h – 2 + 1 für h ≥ 2 und n 0 = 0 , n 1 = 1 . Die minimalen AVL-Bäume der Höhen h = 0, 1, 2, 3, 4 haben bis auf Symmetrie folgende Gestalt: Die Rekursionsgleichung für n h erinnert an die Definition der Fibonacci-Zahlen: n fib(n) = fib(n-1) + fib(n-2) Universität München, Institut für Informatik, Hans-Peter Kriegel für n ≤ 1 für n > 1 Studienjahr 2002 Informatik II - 182 h 0 1 2 3 4 5 6 nh 0 1 2 4 7 12 20 fib(h) 0 1 1 2 3 5 8 Hypothese: n h = fib(h + 2) – 1 Beweis: Induktion über h Induktionsanfang: n 0 = fib(2) – 1 = 1 – 1 = 0 ✓ Induktionsschluß: n h + 1 = n h + n h – 1 + 1 = 1 + fib(h + 2) – 1 + fib(h + 1) – 1 = fib(h + 3) – 1 1 1+ 5 1– 5 n n Hilfssatz: fib(n) = ------- ⋅ ( ϕ 1 – ϕ 2 ) mit ϕ 1 = ----------------, ϕ 2 = ---------------- ≈ – 0,618 . 2 2 5 Der Beweis erfolgt durch vollständige Induktion. (wird hier übergangen) Für jede beliebige Schlüsselanzahl n ∈ IN gibt es ein eindeutiges h max(n) mit: n hmax(n) ≤ n < n hmax(n) + 1 . Mit obiger Hypothese folgt hieraus: n + 1 ≥ fib(h max(n) + 2) . Durch Einsetzen erhalten wir: h (n) + 2 1 1 ≥ ------ ⋅ ϕ 1 max – --ϕ2 2 5 < 0,62 < 5 ⁄ 2 h max(n) + 2 1 h max(n) + 2 n + 1 ≥ ------- ⋅ ϕ 1 – 5 h (n) + 2 1 3 Und damit: ------- ⋅ ϕ 1 max ≤ n + --- . 2 5 1 3 Durch Auflösen nach h max(n) ergibt sich: log ϕ1(-------) + h max(n) + 2 ≤ log ϕ1(n + ---) . 2 5 Und für h max(n) : 3 1 hmax(n) ≤ log ϕ1(n + ---) – log ϕ1(-------) + 2 ≤ 2 5 ≤ log ϕ1(n) + const = log ϕ1(2) ⋅ log 2(n) + const c(a) log (a) = log ---------------b logc(b) ln 2 = ----------- ⋅ log 2(n) + const ≈ 1,44 ⋅ log 2(n) + const ln ϕ 1 Für große Schlüsselanzahlen ist die Höhe eines AVL-Baumes somit um maximal 44% größer als die des vollständig ausgeglichenen binären Suchbaumes. Also gilt die Behauptung: h max(n) = O(log n) . Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 183 Einfügen von Schlüsseln: Insert(k) (In der folgenden Beschreibung sind symmetrische Fälle nicht dargestellt.) Der Schlüssel k wird in einen neuen Sohn K des Knotens B eingefügt. Fall 1: B ist ein Blatt B B +* = K = * und UP(B) K = Fall 2: B hat einen linken Sohn B B - = STOP A = K = * A = K = Die Methode UP(S) wird aufgerufen für einen Knoten S, dessen Teilbaum in seiner Höhe um 1 gewachsen ist. S ist die Wurzel eines korrekten AVL-Baumes. ➙ mögliche Strukturverletzung durch einen zu hohen Teilbaum! S Fall 1: der Vater von S hat BF ‘= ‘ 1.1 der Vater von S ist nicht die Wurzel A +* A = S * S und UP(A) 1.2 der Vater von S ist die Wurzel: ➙ dieselbe Transformation und STOP. Fall 2: der Vater von S hat BF ‘+’ oder ‘-’ und S ist die Wurzel des kürzeren Teilbaumes. ➙ In beiden Fällen wird der BF im Vater zu ‘=’ und STOP. Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 184 Fall 3: der Vater von S hat BF ‘+’ oder ‘-’ und S ist die Wurzel des höheren Teilbaumes. 3.1 der Vater von S hat BF ‘+’ und S hat BF ‘+’: A + S + T1 S = * A= T1 T2 T2 T3 STOP T3 Rotation 3.2 der Vater von S hat BF ‘+’ und S hat BF ‘-’. z.B.: B hat BF ‘-’. A + S - T1 B = * A= B - T1 T2 +S T3 STOP T4 T4 T3 T2 Doppel-Rotation der Fall B hat BF ‘+’ wird analog gehandhabt. 3.3 der Vater von S hat BF ‘-’ und S hat BF ‘-’: ➙ symmetrisch zu 3.1 3.4 der Vater von S hat BF ‘-’ und S hat BF ‘+’: ➙ symmetrisch zu 3.2 Beim Einfügen genügt eine einzige Rotation bzw. Doppelrotation um eine Strukturverletzung zu beseitigen. Wir werden sehen, daß dies beim Entfernen nicht genügt. Entfernen von Schlüsseln: Delete(k) (In der folgenden Beschreibung sind symmetrische Fälle nicht dargestellt.) Der Knoten K mit Schlüssel k wird entfernt. Fall 1: K hat höchstens einen Sohn 1.1 K ist ein Blatt 1.1.1 K hat keinen Bruder B K - B = * Universität München, Institut für Informatik, Hans-Peter Kriegel = * und UP’(B) Studienjahr 2002 Informatik II - 185 1.1.2 K hat einen Bruder B = K = * A = oder B STOP A = D K = * B = A = C - C = oder B - D = STOP A = D - C = K = * B + B = D = und UP’(C) C = B = * C - oder K = * B - A = C = und UP’(B) A = 1.2 K hat genau einen Sohn 1.2.1 K hat einen linken Sohn K B - * B = = * und UP’(B) 1.2.2 K hat einen rechten Sohn: ➙ symmetrisch Fall 2: K ist ein innerer Knoten (hat zwei Söhne) Man bestimme in dem AVL-Baum den kleinsten Schlüssel s, der größer als k ist. s ist in einem Halbblatt S. Ersetze k durch s und entferne den Schlüssel s. ➙ damit haben wir Fall 2 auf Fall 1 zurückgeführt. Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 186 Die Methode UP’(S) wird aufgerufen für einen Knoten S, dessen Teilbaum in seiner Höhe um 1 reduziert ist. Der Teilbaum mit Wurzel S ist ein korrekter AVL-Baum. ➙ mögliche Strukturverletzung durch einen zu niedrigen Teilbaum! S * Fall 1: der Vater von S hat BF ‘=’. B - B= * S S STOP Fall 2: der Vater von S hat BF ‘+’ oder ‘-’ und S ist die Wurzel des höheren Teilbaums. B =* B+ S * S und UP’(B) Fall 3: der Vater von S hat BF ‘+’ oder ‘-’ und S ist die Wurzel des kürzeren Teilbaums. 3.1 der Vater von S hat BF ‘+’ 3.1.1 der Bruder von S hat BF ‘=’ U+ S V U+ V = * T2 S T1 STOP T1 T2 Rotation 3.1.2 der Bruder von S hat BF’+’. V =* U+ U= V + S * S T1 T2 und UP’(V) T1 T2 Rotation Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 187 3.1.3 der Bruder von S hat BF’-’. V =* U + S U W - * V W S und UP’(V) T1 T2 T3 T3 T1 T2 Doppel-Rotation Mindestens einer der beiden Bäume T1 und T2 hat die durch den hell schraffierten Bereich angegebene Höhe. 3.2 der Vater von S hat BF ‘-’: ➙ symmetrisch zu 3.1. Fall 4: S ist die Wurzel. ➙ STOP Im Falle von Entferne-Operationen wird eine mögliche Strukturverletzung also nicht notwendigerweise durch eine einzige Rotation bzw. Doppelrotation beseitigt. Im schlechtesten Fall muß auf dem Suchpfad bottom-up vom zu entfernenden Schlüssel bis zur Wurzel auf jedem Level eine Rotation bzw. Doppelrotation durchgeführt werden. Korollar: Die AVL-Bäume bilden eine Klasse balancierter Bäume. 8.4 B-Bäume Sei n die Anzahl der Objekte und damit der Datensätze. Wir nehmen nun an, daß das Datenvolumen zu groß ist, um im Hauptspeicher gehalten zu werden, z.B. n = 106. ➙ Datensätze auf externen Speicher auslagern, z.B. Plattenspeicher. Beobachtung: Der Plattenspeicher wird als Menge von Blöcken (mit einer Größe im Bereich von 1 - 4 KByte) betrachtet, wobei ein Block durch das Betriebssystem jeweils komplett in den Hauptspeicher übertragen wird. Diese Übertragungseinheiten werden auch als Seiten des Plattenspeichers bezeichnet. Idee: Konstruiere eine Baumstruktur mit der Eigenschaft: ^ 1 Seite (bzw. mehreren Seiten) des Plattenspeichers 1 Knoten des Baumes = Für binäre Baumstrukturen bedeutet das: ^ 1 Plattenspeicherzugriff left oder right-Zeiger folgen = Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 188 Beispiel: Sei die Anzahl der Datensätze: n = 106 6 3 2 3 ➙ log 2(10 ) = log2(10 ) = 2 ⋅ log2(10 ) ≈ 20 Plattenspeicherzugriffe Idee: Zusammenfasssen mehrerer binärer Knoten zu einer Seite Übertragungseinheit (“Seite”) enthält hier 7 Knoten Beispiel für einen B-Baum: 99 Knoten in einer Seite ➙ 100-fache Verzweigung: 6 ➙ log 100(10 ) = 3 Plattenspeicherzugriffe. (reduziert auf 2, falls die Wurzelseite immer im Hauptspeicher liegt) Definition: B-Baum der Ordnung m (Bayer und McCreight (1972)) (1) Jeder Knoten enthält höchstens 2m Schlüssel. (2) Jeder Knoten außer der Wurzel enthält mindestens m Schlüssel. (3) Die Wurzel enthält mindestens einen Schlüssel. (4) Ein Knoten mit k Schlüsseln hat genau k+1 Söhne. (5) Alle Blätter befinden sich auf demselben Level. Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 189 Beispiel: für einen B-Baum der Ordnung 2 K C F A B O T D E G I J L M P Q RS V WX Z Berechnung der maximalen Höhe h max eines B-Baumes der Ordnung m mit n Schlüsseln: Level 1 hat k1 = 1 Knoten Level 2 hat k2 ≥ 2 Knoten Level 3 hat k3≥ 2(m+1) Knoten . . Level h+1 hat . . kh+1 ≥ 2(m+1)h-1 (äußere, leere) Knoten Ein B-Baum mit n Schlüsseln teilt den Wertebereich der Schlüssel in n + 1 Intervalle. Es gilt also n+1 kh+1 = n + 1 ≥ 2(m+1)h-1, d.h. h ≤ 1 + log m + 1 ------------ . 2 Da die Höhe immer ganzzahlig ist, und da diese Rechnung minimalen Füllgrad annimt, folgt: n+1 h ≤ log m + 1 ------------ + 1 2 Beobachtung: Jeder Knoten (außer der Wurzel) ist mindestens mit der Hälfte der möglichen Schlüssel gefüllt. Die Speicherplatzausnutzung beträgt also mindestens 50 %! Allgemeine Knotenstruktur: p0, k 1,p1 , k2, . . . . ,pj-1 , kj, pj wobei: k 1 < k 2 < … < k j und m ≤ j ≤ 2m ; pi zeigt auf den Teilbaum mit Schlüsseln zwischen k i und k i + 1 . pi ki < Schlüssel < ki+1 Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 190 Anmerkung: Da die Schlüssel in jedem Knoten eines B-Baumes aufsteigend sortiert sind, kann ein Knoten nach Übertragung in den Hauptspeicher binär durchsucht werden. class Entry { public int key; public Page son; ... // Konstruktor.. } class Page { public int numberOfEntries; public Page firstSon; public Entry [] entries; // Im Konstruktor intialisieren mit new Entry [2*m+1]; ... } class Btree { protected Page root; protected int m; ... } Welche Ordnung m von B-Bäumen ist auf realen Rechnern und Plattenspeichern günstig? Hierzu betrachten wir zunächst den physischen Aufbau eines Magnetplattenspeichers. Dieser besteht aus einer Reihe übereinanderliegender rotierender Magnetplatten, die in Zylinder, Spuren und Sektoren unterteilt sind. Der Zugriff erfolgt über einen Kamm mit Schreib-/Leseköpfen, der quer zur Rotation bewegt wird. Kammbewegung Spur Kamm Zylinder SchreibLesekopf Rotation Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 191 Der Seitenzugriff erfolgt nun in mehreren Phasen. Etwas vereinfacht läßt sich die Zugriffszeit in die Zeiten für die folgenden Phasen zerlegen: • Positionierung des Schreib-/Lesekopfes: Positionierungszeit (PZ) Zeit für die Kammbewegung 6 ms • Warten auf den Sektor / die Seite: Latenzzeit (LZ) Im Mittel halbe Rotationszeit der Platte 4 ms • Übertragung der Seite: Übertragungszeit (ÜZ) Zeit für das Schreiben / Lesen 1·10-4 ms / Byte ➙ Zugriffszeit für eine Seite: PZ + LZ + ÜZ · (Seitengröße). Sei die Größe eines Schlüssels durch α Bytes und die eines Zeigers durch β Bytes gegeben. ➙ Seitengröße ≈ 2m ( α + β ) ≈ 2m ( α + β ) = a + b·m mit: a = PZ + LZ und b = 2(α+β) · ÜZ. ➙ Zugriffszeit pro Seite = PZ + LZ + ÜZ · Andererseits ergibt sich für die interne Verarbeitungszeit pro Seite bei binärer Suche: c ⋅ log 2(m) + d für Konstanten c und d. Die gesamte Verarbeitungszeit pro Seite ist damit: a + b ⋅ m + c ⋅ log 2(m) + d . Die maximale Anzahl von Seiten auf einem Suchpfad eines B-Baumes mit n Schlüsseln ist: hmax = n+1 log 2(------------) n+1 2 log m + 1(------------) + 1 = f ⋅ -------------------------- für eine Konstante f. 2 log 2(m) Die maximale Suchzeit MS ist damit gegeben durch die Funktion: a+d b⋅m n+1 MS(m) = g ⋅ ------------------ + ------------------ + c mit g = f ⋅ log 2(------------) . log 2(m) log 2(m) 2 Für die obigen Konstanten setzen wir beispielhaft die folgenden Werte ein: a = 0,01s, a + d ≈ a = 0,01s . = 10 ms –4 –4 α = 8, β = 4 ➙ b = 24 ⋅ 1 ⋅ 10 ms = 24 ⋅ 10 ms . Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 192 Damit ist die folgende Funktion MS(m) zu minimieren: 10 0,0024 ⋅ m MS(m) = ------------------ + ------------------------- ms ➙ MIN. log 2(m) log2(m) MS(m) 1.5 1.25 600 750 m 900 Die maximale Suchzeit ist somit nahezu minimal für 600 ≤ m ≤ 900 . Dies entpricht in obigem Beispiel einer “optimalen” Seitengröße von 12 KByte. Einfügen in B-Bäumen: Zunächst wird der Knoten K gesucht, in den der neue Schlüssel einzufügen ist. Dieser ist stets ein Blatt. Der Schlüssel wird in K an der entsprechenden Stelle eingefügt. Sei s die Anzahl der Schlüsel in K nach dem Einfügen: Fall 1: s ≤ 2m : Fall 2: s = 2m + 1 : ➙ OVERFLOW ➙ STOP Ein OVERFLOW wird behandelt durch Aufspalten (SPLIT) des Knotens. Dies kann einen OVERFLOW im Vaterknoten zur Folge haben. Auf diese Weise kann sich ein OVERFLOW bis zur Wurzel des Baumes fortsetzen. Falls die Wurzel gesplittet wird wächst die Höhe des Baumes um 1. möglicher OVERFLOW im Vaterknoten SPLIT: km+1 * k1 k2 k3 ... k2m+1 k1 k2 ... Universität München, Institut für Informatik, Hans-Peter Kriegel km km+2 . . . k2m+1 Studienjahr 2002 Informatik II - 193 Entfernen aus B-Bäumen: Der zu entfernende Schlüssel k wird im Baum gesucht und aus dem gefundenen Knoten K gelöscht. Falls K kein Blatt ist, wird der entfernte Schlüssel durch den Schlüssel p ersetzt, der der kleinste Schlüssel im Baum ist, der größer als k ist. Sei P der Knoten, in dem p liegt. Dann ist P ein Blatt, aus dem p nun entfernt wird. Auf diese Weise wird der Fall “Löschen in einem inneren Knoten” auf den Fall “Löschen in einem Blatt” zurückgeführt. Sei s die Anzahl der Schlüssel in K nach dem Entfernen: Fall 1: s ≥ m : ➙ STOP Fall 2: s = m – 1 : ➙ UNDERFLOW UNDERFLOW-Behandlung: Fall 1: Der Bruder hat ≥ m+1 Schlüssel: ➙ Ausgleichen mit dem Bruder #=r #=r k l STOP * #=m-1 l #=m k #=b-1 # = b (>m) Fall 2: Der Bruder hat m Schlüssel: ➙ Verschmelzen mit dem Bruder unter Hinzunahme des trennenden Vaterschlüssels ➙ möglicher UNDERFLOW im Vaterknoten. # = r - 1 möglicher UNDERFLOW im Vaterknoten #=r k * #=m-1 #=m #=m-1 k #=m # = 2m So kann sich auch die UNDERFLOW-Behandlung bis zur Wurzel des Baumes fortsetzen. Wird aus der Wurzel des Baumes der letzte Schlüssel entfernt, so wird diese gelöscht; die Höhe des Baumes verringert sich damit um 1. Korollar: Die B-Bäume bilden eine Klasse balancierter Suchbäume. Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002 Informatik II - 194 Aufwandsabschätzung für die durchschn. Anzahl von Aufspaltungen (Splits) pro Einfügung: Bezeichne s die durchschnittliche Anzahl der Knoten-Splits pro Einfügung: Modell: Ausgehend von einem leeren Baum wird ein B-Baum der Ordnung m für die Schlüssel k 1, k 2, …, k n durch n aufeinanderfolgende Einfügungen konstruiert. Sei t die Gesamtzahl der Aufspaltungen bei der Konstruktion dieses B-Baumes ➙s = t ⁄ n. Sei p die Anzahl der Knoten (Seiten) des B-Baumes mit p ≥ 3 ➙ t < p – 1 (genauer: t ≤ p – 2 ) Für die Anzahl n der Schlüssel in einem B-Baum der Ordnung m mit p Knoten gilt: n ≥ 1 + (p – 1) ⋅ m . t p –1 1 n–1 1 n –1 ➙ p – 1 ≤ --------- ➙s = --- < --------- ≤ --- ⋅ --------- < ---- wobei im allg.: 600 ≤ m ≤ 900 (s.o.). m n n n m m Es gilt: t ≤ p - 2 ➙ t < p - 1 für p ≥ 3 Denn: - bei jeder Aufspaltung wird mindestens ein zusätzlicher Knoten geschaffen - Aufspalten der Wurzel ➙ 2 zusätzliche Knoten - wenn ein B-Baum mehr als einen Knoten hat, dann ist die Wurzel mindestens einmal aufgespalten worden - dem ersten Knoten des B-Baums geht keine Aufspaltung voraus. Universität München, Institut für Informatik, Hans-Peter Kriegel Studienjahr 2002