Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Bäume, Suchbäume und Hash-Tabellen Im folgenden Fokus auf Datenstrukturen, welche den assoziativen Zugriff (über einen bestimmten Wert als Suchkriterium) optimieren Bäume: Abbildung bzw. Vorberechnung von Entscheidungen während der Suche in einer geordneten Menge als hierarchische Datenstruktur (Entscheidungsbaum, Suchbaum) Hash-Tabellen: Abbildung von Objekten auf den Speicher (deren Position darin) wird direkt aus dem Suchkriterium als Eigenschaft der Objekte abgeleitet Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–34 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Bäume Grundlegende Datenstruktur zur Abbildung einer Hierarchie Setzt Grundprinzip „Teile und Herrsche“ (siehe Algorithmen (→) als Datenstruktur um: Zerlegung von großen Datenmengen in kleinere, besser handhabbare Grundstruktur: ausgehend von einer Wurzel (Gesamtheit) kommt man über verschiedene Verzweigungen (Unterteilungen) zu den Blättern (kleinste Einheiten) Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–35 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Allgemeine Struktur von Bäumen Höhe des Baumes Wurzel Innere Knoten 1 2 3 4 Blätter Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–36 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Beispiele: Baumstrukturen in der Informatik Dateisysteme mit Festplatten, Verzeichnissen, wiederum darin enthaltenen Verzeichnissen und letztendlich Dateien Dokumentenstrukturen, z.B. Mit Kapiteln, Abschnitten, Absätzen ◮ HTML und XML als hierarchische Strukturen Syntaktische Analyse und Auswertung von Programmen/Termen: Zerlegung eines Satzes einer Sprache (Grammatik) enstprechend Regeln in Teilausdrücke/Wortgruppen bis hin zu kleinsten Einheiten (Atome, Terminale) Suchbäume als Indexe zum schnellen assoziativen Zugriff über Schlüsselwerte ◮ ◮ ◮ Datenbanksysteme Allgemein: Suche nach Worten in Texten Speziell: Suchmaschinen im World Wide Web Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–37 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Binäre Suchbäume 12 5 2 17 15 11 14 19 18 21 Binär = Verzweigungsgrad 2: jeder Knoten hat maximal 2 Kindknoten Jeder Knoten speichert einen Suchschlüssel und repräsentiert damit folgende Entscheidung: ◮ ◮ ◮ Ist der gesuchte Wert gleich dem Schlüssel → GEFUNDEN Ist der Wert kleiner, gehe zum linken Kindknoten Ist der Wert größer, gehe zum rechten Kindknoten Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–38 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Binäre Suchbäume: Suchen und Einfügen Suchen und Einfügen prinzipiell ähnlich: ◮ ◮ ◮ Algorithmus startet an der Wurzel In jedem Knoten: wenn Schlüssel nicht gefunden, verzweige zu einem Kindknoten Auf Blattebene: ⋆ ⋆ Einfügen: neuen Kindknoten erzeugen Suchen: Worst Case - Schlüssel nicht gefunden Aufwand für beide Operationen dominiert vom Durchlaufen des Weges von der Wurzel bis zum Blatt, d.h. Höhe des Baumes an dieser Stelle Balancierter Baum (→): Baum ist so gleichmäßig gefüllt, dass Weg von der Wurzel zu Blättern überall möglichst gleich Bei balanciertem Baum mit n = 2k Elementen ist die Höhe des Baumes ca. h = k = log2 n Durchschnittlicher Aufwand für beide Operationen damit: O(log n) Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–39 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Balancierte Binäre Suchbäume: Aufwand Eike Schallehn Maximale #Knoten Höhe = Aufwand 1= 2^1-1 3=2^2-1 7=2^3-1 15=2^4-1 31=2^5-1 63=2^6-1 127=2^7-1 255=2^8-1 511 1023 ... n 1 2 3 4 5 6 7 8 9 10 ... O(log n) Grundlagen der Informatik für Ingenieure 2008/2009 5–40 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Binärbaum: Beispielimplementierung (C++) Einfach Implementierung bestehend aus Klassen für ◮ ◮ Knoten mit Schlüssel und Verweisen auf Kindknoten Binärbaum mit Verweis auf Wurzeln Implementiert nur Suchen und Einfügen Eigentliche Daten werden nicht eingetragen, nur Schlüssel vom Typ int Hinweise ◮ ◮ Verwendet friend-Klassen: umgehen Kapselung, indem befreundete Klassen auf privat-Daten zugreifen können Vollständiger Quelltext auf der Web-Seit zur Vorlesung Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–41 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Binärbaum: Knotenklasse class Node { friend class BinaryTree; private : int key; Node* left; Node* right; Node(int k) { ... } bool search(int k); void insert(int k); void print(int level); }; Definiert rekursive Methoden zum Einfügen und Suchen → Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–42 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Binärbaum: Baumklasse class BinaryTree { public : BinaryTree() { root = NULL; } bool search(int key); void insert(int key); void print(); private : Node* root; }; Methoden zum Einfügen und Suchen als Einstiespunkt für Rekursion ausgehend von der Wurzel Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–43 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Binärbaum: Einfügen void Node::insert(int k) { if (k==key) return; if (k<key) if (left != NULL) left->insert(k); else left = new Node(k); if (k>key) if (right != NULL) right->insert(k); else right = new Node(k); } Schlüssel vorhanden → Einfügen beenden Andernfalls, falls möglich im linken (neuer Schlüssel kleiner) oder rechten Teilbaum einfügen (neuer Schlüssel größer) Falls kein Kindknoten links oder rechts existiert: neuen Kindknoten mit neuem Schlüssel erzeugen Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–44 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Entartung von Bäumen Balanciertheit wichtige Eigenschaft von Bäumen: garantiert effiziente Ausführung der Operationen mit O(log n) Ohne weiteres aber keine garantierte Eigenschaft Abhängig zum Beispiel von Einfügereihenfolge Schlechte Einfügereihenfolge kann zu Entartung des Baumes führen Im schlimmsten Fall wird Baum zu Liste Operationen dann mit wesentlich schlechterer Laufzeitkomplexität O(n): sequentielle Suche Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–45 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Beispiel: Entartung von Bäumen 1 4 2 2 1 6 3 5 3 4 7 5 6 Balancierter Baum bei Einfügereihenfolge 4, 2, 6, 3, 1, 7, 5 Eike Schallehn Entarteter Baum bei Einfügereihenfolge 1, 2, 3, 4, 5, 6, 7 Grundlagen der Informatik für Ingenieure 2008/2009 7 5–46 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Balancierte Bäume Sicherstellung einer relativen Ausgeglichenheit bei binären Bäumen durch spezielle Modifikationsoperationen (Einfügen, Löschen) ◮ ◮ Angabe eines speziellen Balancekriteriums, z.B. AVL-Baum: in jedem Knoten darf der Höhenunterschied zwischen linkem und rechten Teilbaum maximal 1 sein! Wird Balancekriterium verletzt, werden Verfahren zur lokalen Reorganisation des Baumes angewandt → AVL-Bäume, Rot-Schwarz-Bäume Vollständige Ausgeglichenheit möglich durch Knoten mit variablem Verzweigungsgrad ◮ ◮ ◮ Mehr als 1 Schlüssel pro Knoten Verweis auf Kindknoten mit Werten zwischen 2 Schlüsseln (Bereich) Knotengröße kann an Speicherstrukturen angepasst werden (z.B. Blöcke der Festplatte) → B-Bäume Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–47 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen ... geht es besser als O(log n)? Assoziative Zugriffe (Suche über einen Schlüsselwert) mit Bäumen mit logarithmischem Aufwand O(log n) D.h. nur ein zusätzlicher Suchschritt notwendige für jede Verdopplung der Größe der Datenmenge, in der gesucht wird Geht es noch besser? Ja, Hash-Tabellen können Schlüsselzugriff (unter bestimmten Bedingungen) mit konstantem Aufwand O(1) umsetzen D.h. egal wie groß die Datenmenge, das Finden der richtigen Daten geht immer gleich schnell! Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–48 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Hash-Tabellen Auch Streuwerttabelle oder Hash Map Grundprinzip: Berechnung der Position der Daten im Speicher (strukturiert als Tabelle) aus dem Schlüsselwert key ◮ ◮ Berechnung der Position beim Einfügen Berechnung der Position beim Suchen Erfordert Vorreservierung eines Speicherbereichs der Größe M → M meist sehr groß, ab mehreren Tausend Einträgen Positionen 0 . . . M − 1 in Speicherbereich werden auch Hash Buckets genannte Berechnung der Position über spezielle Hash-Funktion h : dom(key) → {0, 1, . . . , M − 1} Wahlfreier Zugriff im RAM und auf Festplatte ermöglicht direkten Zugriff auf an dieser Stelle gespeicherte Daten Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–49 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Einfügen in Hash-Tabellen Zu speichernde Objekte Hash-Tabelle Student Udo Urban MatrNr 170481 0 1 2 Student Eva Lange MatrNr 175783 156324, Max Müller 170481, Udo Urban 3 4 Student Max Müller MatrNr 156324 5 6 175783, Eva Lange Hash-Funktion h(MatrNr)=MatrNr % 7 Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–50 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Suchen in Hash-Tabellen Hash-Tabelle 0 Suche nach Matrikelnummer: 1 170481 3 2 156324, Max Müller 170481, Udo Urban 4 5 6 175783, Eva Lange Hash-Funktion h(MatrNr)=MatrNr % 7 Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–51 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Hash-Funktionen Wertebereich ist (beim hier betrachteten statischen Hashen) durch Speichergröße M bestimmt Problem: Hash-Funktion ist nicht injektiv, d.h. verschiedene Schlüssel können auf eine Adresse abgebildet werden → Kollisionen! Gute Hash-Funktionen erzeugen möglichst zufällig gestreute Speicherzuordnung und machen dadurch Kollisionen unwahrscheinlich Meist umgesetzt durch ◮ ◮ ◮ Kombination von verschiedenen Operationen mit möglichst zufälligem Ergebnis, z.B. Bit-Verschiebeoperationen Am Ende Modulodivision durch M → Rest ist Hash-Wert Primzahlen als Parameter der Hash-Funktion sorgen für gute, zufällige Verteilung Kollisionen lassen sich aber meist nicht völlig vermeiden → erfordern Kollisionsbehandlung Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–52 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Hash-Tabellen: Kollisionsbehandlung Verkettete Liste: der Eintrag in einer Hash-Tabelle verweist auf eine Liste der dorthin gehashten Daten Kann bei schlechter Hash-Funktion mit vielen Kollisionen zu Entartung führen Mehraufwand für Speicherung Sondieren: (engl. Probing) ist der Hash Bucket bereits belegt, wird nach einem einfachen Muster ein anderer Platz gesucht Z.B. lineares Sondieren: testen ob folgende Hash Bucket frei ist, erster freier wird genutzt Doppeltes Hashen: ist der Hash Bucket belegt, wird (ggf. wiederholt) ein weiterer Hash-Wert berechnet und diese Position getestet Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–53 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Implementierung von Hash-Tabellen (Bisher besprochene) statische Hash-Verfahren: vordefinierte Speichergröße kann effizient über Array umgesetzt werden Dynamische Hash-Verfahren können den benutzten Speicherbereich zur Laufzeit Vergrößern → z.B. durch verkettete Arrays Kollisionsbehandlung durch verkettet Liste erfordert zusätzliche Datenstruktur Sondieren und Doppeltes Hashen Erfordern aufwändigere Operationsimplementierungen Beispielimplementierung auf der Web-Seite zur Vorlesung ◮ ◮ Einfaches statisches Hashverfahren mit linearem Sondieren In der Vorlesung nicht vorgestellt Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–54 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Nachteile von Hashtabellen Keine Ordnung der Elemente: in Bäumen sind Elemente stets geordnet gespeichert – geordnete Ausgabe aus einer Hash-Tabelle erfordert zusätzliche Sortierung Vorreservierung des Speichers notwendig: z.B. über Arrays, die zur Vermeidung von Überlauf und Kollisionen ggf. weit überdimensioniert sind (Trade-off: Speichereffizienz vs. Laufzeiteffizienz) Überlauf möglich: bei einigen statischen Verfahren (z.B. bei Überlaufbehandlung durch Sondieren, nicht bei verketteter Liste) kann die Hash-Tabelle tatsächlich vollständig gefüllt werden, so dass keine weiteren Daten eingetragen werden können Aufwand für Dynamik: Verfahren, welche zur Vermeidung von Überläufen und Kollisionen, die Hash-Tabelle dynamisch wachsen lassen, nähern sich mit ihrem Laufzeitverhalten Bäumen an Aufwand für Überlaufbehandlung: auch bei vielen Kollisionen, z.B. durch schlechte Hash-Funktion, verschlechtert sich die Laufzeitkomplexität Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–55 Grundlegende Datenstrukturen Bäume, Suchbäume und Hash-Tabellen Zusammenfassung: Datenstrukturen Klassische Datenstrukturen bieten Standardlösungen für effiziente Bearbeitung von Daten Wichtigste hier vorgestellt: ◮ ◮ ◮ Kollektionsdatentypen wie Listen, Mengen und Multimengen zur Verwaltung einer Sammlung zusammengehörender Objekte Queues und Stacks zur Steuerung der Berabeitungsreihenfolge von Datenobjekten Bäume und Hash-Tabellen für schnelles Suchen von Daten über einen Schlüsselwert Oft in Form von generischen Klassenbibliotheken umgesetzt, z.B. STL in C++ Eigene Implementierung durch Verwendung von Typkonstruktoren (Arrays, Structs, Klassen) und Zeiger sowie Klassenschablonen (Templates, Generics) möglich Eike Schallehn Grundlagen der Informatik für Ingenieure 2008/2009 5–56