Datenstrukturen Sommersemester 2010 Isolde Adler Herzlich willkommen! (a, b)-Bäume Wiederholung Hashing Splay-Bäume • Binäre Suchbäume als Grundstruktur. Implementiere lookup, insert und remove durch eine einzige Operation, die Splay-Operation. • Wenn nach einem Schlüssel x gefragt wird, dann wird der Schlüssel durch die Splay-Operation zur Wurzel gebracht, Suchpfad-Eigenschaft: die Knoten auf dem Suchpfad rücken der Wurzel um fast die Hälfte näher. • Nachfolgende Operationen für andere Schlüssel werden Schlüssel x langsam tiefer und tiefer nach unten drücken. Wenn aber nach einiger Zeit Schlüssel x wieder abgefragt wird, wird er sofort wieder zur Wurzel gebracht. • Wenn zwischen sukzessiven Anfragen nach x nicht zuviel Zeit vergeht, ist jede Abfrage schnell, da x nicht tief im Baum sitzt. Wenn n Splay-Operationen (in einem anfänglich leeren Splay-Baum) durchgeführt werden, beträgt die worst-case Laufzeit O (n log2 n). Die Laufzeit einer einzelnen Operation kann allerdings bis zu O(n) Schritte verschlingen. Isolde Adler Datenstrukturen 2010 2/19 (a, b)-Bäume Wiederholung Hashing Splay-Bäume: Anwendungen • Überall, wo sich das Wörterbuch“ den Benutzeranfragen anpassen ” soll • Caches • Suchmaschinen • Web-Browser • DNS: IP-Adressen • Datenbanken • Datenkompression Isolde Adler Datenstrukturen 2010 3/19 Wiederholung (a, b)-Bäume Hashing (a, b)-Bäume – Idee Bisher: binäre Bäume für das Wörterbuch. Jetzt: • mehr als zwei Nachfolger erlauben geringere Tiefe! • mehr als einen Schlüssel pro Knoten speichern! • Vorteile von binärer Suche beibehalten • Der Baum soll nicht so oft umkonfiguriert werden (a, b)-Bäume: Vorteilhaft bei großen Datenmengen auf Hintergrundspeicher: • Nur ein Bruchteil der Daten passt in den Hauptspeicher • Anzahl der teuren Zugriffe auf den Hintergrundspeicher klein halten! • Daten auf dem Hintergrundspeicher können nur blockweise gelesen werden • Ein Knoten des Baumes wird als Block gelesen (a, b)-Bäume verallgemeinern B-Bäume • Codd 1970: führt relationale Datenbanken ein (s. auch Vorlesung Logik und Datenbanken) • Bayer 1972: entwickelt B-Bäume für Verwaltung (großer) relationaler Datenbanken • erste SQL-Datenbank bei IBM! Isolde Adler Datenstrukturen 2010 4/19 (a, b)-Bäume Wiederholung Hashing Die (a, b)-Eigenschaft Es gelte a ≥ 2 und b ≥ 2a − 1. Ein Baum T hat die (a, b)-Eigenschaft, falls • alle Blätter von T die gleiche Tiefe haben, • alle Knoten höchstens b Kinder besitzen und • die Wurzel mindestens zwei Kinder hat, während alle sonstigen Knoten mindestens a Kinder haben. • (a, b)-Bäume haben die (a, b)-Eigenschaft. • Interessant sind (a, b)-Bäume vor Allem für große Werte von a und b, wenn Daten auf einem Externspeicher abgelegt sind: • Die Tiefe wird dementsprechend klein sein • und wenige der sehr langsamen Zugriffe auf den Externspeicher genügen. Isolde Adler Datenstrukturen 2010 5/19 (a, b)-Bäume Wiederholung Hashing Die Suchstruktur von (a, b)-Bäumen T ist ein (a, b)-Baum für die Schlüsselmenge S (bzw. ein B-Baum falls b = 2a − 1), wenn gilt: • T hat die (a, b)-Eigenschaft. • Jeder Schlüssel in S wird in genau einem Knoten von T gespeichert. Ein Knoten speichert die ihm zugewiesenen Schlüssel in aufsteigender Reihenfolge. • Jeder Knoten mit k Kindern speichert genau k − 1 Schlüssel. • Ein Blatt speichert höchstens b − 1 Schlüssel und mindestens a − 1 Schlüssel. • Falls der innere Knoten v die Schlüssel x1 , ..., xc (mit x1 < x2 < · · · < xc und c ≤ b − 1) speichert, dann • speichert der linkeste (bzw. rechteste) Teilbaum nur Schlüssel aus dem Intervall (−∞, x1 ) (bzw. (xc , ∞)). • Der i-te Teilbaum (für 2 ≤ i ≤ c) speichert nur Schlüssel aus dem Intervall (xi−1 , xi ). Isolde Adler Datenstrukturen 2010 6/19 (a, b)-Bäume Wiederholung Hashing Ein Beispiel Für a = 2 und b = 3 erhalten wir 2-3 Bäume: E PP PP P P H, M B @ @ A @ @ D F,G K, L P Die Schlüssel der inneren Knoten helfen in der Suche: • Auf der Suche nach Schlüssel K suche im rechten Teilbaum weiter, denn E < K . • Da H < K < M muss das mittlere Blatt aufgesucht werden. Isolde Adler Datenstrukturen 2010 7/19 (a, b)-Bäume Wiederholung Hashing Die Tiefe von (a, b)-Bäumen T sei ein (a, b)-Baum mit n Knoten. Dann gilt für Tiefe(T ) ≥ 2: logb (n) − 1 < Tiefe(T ) < loga ( n−1 ) + 1. 2 • Die Tiefe ist minimal, wenn jeder Knoten genau b Kinder hat. • In Tiefe t können wir damit höchstens t+1 n ≤ 1 + b + ... + b t = b b−1−1 < b t+1 Knoten erreichen. • Also folgt b t+1 > n und damit t > logb n − 1. • Die Tiefe ist am größten, wenn die Wurzel zwei Kinder und jeder innere Knoten a Kinder besitzt. Wir erhalten also in Tiefe t mindestens t −1 n ≥ 1 + 2(1 + ... + at−1 ) = 1 + 2 · aa−1 Knoten. • Also folgt n ≥ 1 + 2 · Aber at−1 < Isolde Adler t a −1 a−1 at −1 a−1 , beziehungsweise at −1 a−1 gilt für t ≥ 2 und deshalb t < Datenstrukturen 2010 8/19 n−1 2 . loga n−1 2 ≤ + 1. Wiederholung (a, b)-Bäume Hashing Lookup(x) Benutze die den inneren Knoten zugeordneten Schlüssel, um den Schlüssel x zu lokalisieren. • Es genügen Tiefe(T ) + 1 < loga n−1 2 + 2 Speicherzugriffe. • Wähle a als ein Megabyte und n als ein Terabyte. Sei a = 106 und n = 1012 . • Dann genügen weniger als log106 1012 + 2 Zugriffe und damit reichen drei Speicherzugriffe. • Wenn n ein Petabyte (n = 1015 ) ist, dann reichen vier Zugriffe. Dasselbe gilt sogar für ein Exabyte (n = 1018 ). Für die lookup Operation in einem (a, b)-Baum mit n Schlüsseln genügen weniger als loga n−1 2 + 2 Speicherzugriffe. Isolde Adler Datenstrukturen 2010 9/19 (a, b)-Bäume Wiederholung Hashing Insert(x) Zuerst suche nach x. Wenn x gefunden wird, dann überschreibe den Info-Teil, ansonsten endet die Suche in einem Blatt v . Füge x in die sortierte Folge der Schlüssel von v ein. • Fall 1: v hat jetzt höchstens b − 1 Schlüssel. Wir sind fertig, da die (a, b)-Eigenschaft erfüllt ist. • Fall 2: v hat jetzt b Schlüssel x1 < ... < xb und die (a, b)-Eigenschaft ist verletzt. • Ersetze v durch zwei Knoten vlinks (mit den Schlüsseln x1 , . . . , xdb/2e−1 ) und vrechts (mit den Schlüsseln xdb/2e+1 , . . . , xb ). • Es ist 2a − 1 ≤ b. Deshalb gilt a − 1 ≤ b b+1 c − 1 ≤ d b2 e − 1 ≤ b b2 c 2 und vlinks und vrechts besitzen die notwendige Mindestanzahl von Schlüsseln. • Der Schlüssel xdb/2e unterscheidet zwischen vlinks und vrechts . Füge xdb/2e rekursiv im Vater von v ein. Isolde Adler Datenstrukturen 2010 10/19 (a, b)-Bäume Wiederholung Hashing Wann erhöht sich die Tiefe? • Wir fügen zuerst in einem Blatt ein, spalten die Schlüssel unter zwei neuen Knoten auf und fügen rekursiv einen trennenden Knoten beim Vater ein. • Wenn die Wurzel bereits b − 1 Schlüssel speichert und einen trennenden Schlüssel zusätzlich erhält, dann muss sie in zwei Knoten aufgespalten werden. Der trennende Schlüssel der beiden neuen Knoten wird zum einzigen Schlüssel der neuen Wurzel. Wir haben erlaubt, dass die Wurzel zwei oder mehr Kinder hat, um diesen Fall abzufangen. • Auch den Grund für die Bedingung 2 · a − 1 ≤ b haben wir gesehen: Die Aufspaltung eines Knoten mit b Schlüsseln in zwei Knoten mit legaler Schlüsselzahl muss möglich sein. Animation Isolde Adler Datenstrukturen 2010 11/19 (a, b)-Bäume Wiederholung Hashing Remove(x) Zuerst müssen wir nach x suchen. • Angenommen wir finden x in dem inneren Knoten v : • Wir suchen den kleinsten Schlüssel y mit x ≤ y . y wird sich im linkesten Blatt b des entsprechenden Teilbaums von v befinden. • Wir ersetzen in v den Schlüssel x durch y . Das Blatt b hat einen Schlüssel verloren. Setze v = b. • Wenn v ein Blatt ist, entfernen wir x und v verliert einen Schlüssel. • Fall 1: v hat jetzt mindestens a − 1 Schlüssel. Wir sind fertig, da die (a, b)-Eigenschaft erfüllt ist. • Fall 2: v hat jetzt a − 2 Schlüssel. • Zuerst begibt sich Knoten v auf Schlüsselklau“ und stiebitzt, wenn ” möglich, einen Schlüssel von seinem Vater. • Sollte dies nicht möglich sein, wird v mit einem Geschwisterknoten fusioniert. Isolde Adler Datenstrukturen 2010 12/19 (a, b)-Bäume Wiederholung Hashing Schlüsselklau und Fusion Der Knoten v habe die Schlüssel x1 < · · · < xa−2 . • Fall 2.1: Der linke oder rechte Geschwisterknoten hat mindestens a Schlüssel. • Der linke Geschwisterknoten v 0 habe z.B. die Schlüssel y1 < · · · < ya < · · · < yk 0 . • Der Schlüssel z des Vaters trenne v 0 und v , also yk 0 < z < x1 . • v klaut z und hat damit a − 1 Schlüssel. Schlüssel z wird durch Schlüssel yk 0 ersetzt. Fertig! • Fall 2.2: Beide Geschwisterknoten besitzen nur a − 1 Schlüssel. • Der linke Geschwisterknoten v 0 hat die a − 1 Schlüssel y1 < · · · < ya−1 . • Verschmelze v 0 und v . Der bisher trennende Schlüssel z des Vaters ist einzufügen und der fusionierte Knoten hat a − 1 + a − 2 + 1 = 2a − 2 ≤ b − 1 Schlüssel und die Höchstanzahl wird nicht überschritten. • Der Schlüssel z ist rekursiv aus dem Vaterknoten zu entfernen. Isolde Adler Datenstrukturen 2010 13/19 (a, b)-Bäume Wiederholung Hashing Schlüsselklau für innere Knoten • Angenommen wir haben die Remove-Operation rekursiv ausgeführt und haben einen inneren Knoten v erreicht. • v habe einen Schlüssel durch Verschmelzung zweier Kinder verloren. • Ein Geschwisterknoten von v speichere mindestens a Schlüssel. Das Ergebnis des Schlüsselklaus für 2-3 Bäume: X Y Y, Z v A B C v D A X Z B Fusion für innere Knoten geht ähnlich. Isolde Adler Datenstrukturen 2010 14/19 C D (a, b)-Bäume Wiederholung Hashing Zusammenfassung Sei T ein (a, b)-Baum. Dann genügen • Tiefe(T ) + 1 Speicherzugriffe für eine lookup-Operation, • 2· (Tiefe(T ) + 1) Speicherzugriffe für eine insert-Operation • und 5·(Tiefe(T ) + 1) Speicherzugriffe für eine remove-Operation. Beachte, dass Tiefe (T ) < loga ( n−1 2 ) + 1 gilt, falls der Baum T aus n Knoten besteht. • Lookup: Der Weg von der Wurzel zu einem Blatt besteht aus Tiefe(T ) + 1 Knoten. • Insert: Die Knoten des Suchpfads werden zuerst gelesen und dann aufgespalten. • Remove: Tiefe(T ) + 1 Zugriffe sind auf dem Weg nach unten erforderlich. Auf dem Weg nach oben genügen 4 (Tiefe(T ) + 1) Zugriffe, das Schreiben des Knoten und das Lesen/Schreiben eines Geschwisterknoten. Isolde Adler Datenstrukturen 2010 15/19 Wiederholung (a, b)-Bäume Hashing Das Wörterbuch – Hashing Wiederholung: Wahrscheinlichkeitsrechnung (s. Skript Kap. 1.3) Isolde Adler Datenstrukturen 2010 16/19 (a, b)-Bäume Wiederholung Hashing Die Bitvektor-Datenstruktur Das Wörterbuchproblem wird einfacher, wenn die Menge U der möglicherweise einzufügenden Daten in den Hauptspeicher passt. • Benutze die Bitvektor-Datenstruktur: In einem booleschen Array wird für jedes Element u ∈ U in der Zelle f (u) vermerkt, ob u präsent ist. Bis auf die Berechnung von f (u) gelingt damit die Ausführung einer lookup-, insert- oder remove-Operation in konstanter Zeit! • Selbst bei einem kleinen Universum U ist aber die Wahl einer geeigneten Funktion f möglicherweise schwierig. • Zudem ist in praktischen Anwendungen im Allgemeinen das Universum der möglichen Schlüssel zu groß: Wenn Nachnamen als Schlüssel verwandt werden, und selbst wenn nur Nachnamen der Länge höchstens 10 auftreten, gibt es 2610 ≥ 210 · 1010 ≥ 103 · 1010 = 1013 , also mehr als 10 Billionen mögliche Schlüssel! Isolde Adler Datenstrukturen 2010 17/19 (a, b)-Bäume Wiederholung Hashing Hashing Hashing versucht die Schnelligkeit und die Einfacheit der Bitvektor-Datenstruktur auch im allgemeinen Fall zu bewahren. • Sei U die Menge aller möglichen Schlüssel und sei m die Größe einer im Hauptspeicher abspeicherbaren Tabelle. • Eine Funktion h : U → {0, 1, ..., m − 1} heißt eine Hashfunktion. • Zum Beispiel können wir insert (x, info) implementieren, indem wir • h(x) = i berechnen und • (x, info) in Zelle i der Tabelle eintragen. Aber was passiert bei einer Kollision, wenn also Zelle i bereits besetzt ist? Wir beschreiben zwei Hashing-Verfahren, Hashing mit Verkettung und Hashing mit offener Adressierung. Isolde Adler Datenstrukturen 2010 18/19 (a, b)-Bäume Wiederholung Hashing Hashing mit Verkettung Für jede Zelle i wird eine anfänglich leere Liste angelegt. • Jede Liste wird sortiert gehalten. • Für lookup(x): Durchlaufe die Liste von h(x). • Für insert(x) und remove(x): Führe die insert- und remove-Operation für einfach-verkettete Listen aus. Beispiel: Wähle h(x) = (x mod 11) als Hashfunktion. Die Operationen insert(59), insert(18) und insert(125) führen auf die Tabelle 4 - 59 - 125 7 - 18 - - lookup (26) benötigt nur einen Suchschritt: Schlüssel 59 wird gefunden und es wird geschlossen, dass 26 nicht präsent ist. Isolde Adler Datenstrukturen 2010 19/19