Teil X Grundlegende Datenstrukturen Überblick 1 Einführung 2 Datenstrukturen für Kollektionen 3 Queues und Stacks 4 Bäume, Suchbäume und Hash-Tabellen Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 488/543 Datenstrukturen Immer wiederkehrende Anforderungen an Verwaltung von Daten im Haupt- und Sekundärspeicher: typische Anordnungen und Zusammenhänge, typische Operationen und immer möglichst effizient! Vergleichbar Algorithmenmustern für die Verarbeitung von Daten: „klassische“ Datenstrukturen als Muster für effiziente Verwaltung von Daten Darüber hinaus: viele klassische Datenstrukturen oft als direkt wiederverwendbare Implementierungen in Programmiersprachenbibliotheken vorhanden Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 489/543 Beispiele für Datenstrukturen und deren Verwendung Prüfungslisten mit geordneten Studentendaten Knoten- und Kantenlisten in BREP-Modellen Das Inventory einer Computerspielfigur als Menge von Gegenständen Verzeichnisbäume zur Verwaltung von Dateien Straßennetzwerke eines Routenplaners als Graphen Warteschlangen mit Prozessen für die Prozessverwaltung des Betriebssystems Der Programmstack zur Verwaltung lokaler Daten von Funktionen während der Programmausführung B-Bäume als Indexe für schnelle Zugriffe in Datenbanksystemen (→) Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 490/543 Definition: Datenstrukturen Definition (Datenstruktur) Eine Datenstruktur ist eine Anordnungsvorschrift zur Organisation und Speicherung von Daten, die für bestimmte Klassen von Anwendungen einen effizienten Zugriff ermöglicht. Umfasst zwei wesentliche Aspekte: Schnittstelle: Festlegung der möglichen Operationen und des Verhaltens als abstrakte Spezifikation (Abstrakte Datentypen →) oder konkrete Programmierschnittstelle (z.B. Bibliotheken wie C++ Standard Template Library →) Implementierung: konkrete Umsetzung in einer Programmiersprache durch möglichst effiziente Speicherstrukturen und Algorithmen Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 491/543 Abstrakte Datentypen Abstrakte Datentypen (ADTs) als implementierungsunabhängige Spezifikationmethode der Schnittstelle und der Semantik Beispiel: Menge type Set[Item] operators create: → Set is_empty: Set → Bool insert: Set × Item → Set is_in: Set × Item → Bool axioms ∀s : Set, ∀i,j : Item is_empty (create) = true is_empty (insert (s, i)) = false is_in (create, i) = false is_in (insert (s, i), j) = if i=j then true else is_in (s, j) insert(insert(s,i),j) = insert(insert(s,j),i) insert(insert(s,i),i) = insert(s,i) Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 492/543 Eigenschaften von Datenstrukturen Datenstrukturen sind ... ... komplex: werden durch Typkonstruktoren (mit Zeigern, Feldern, Strukturen, Klassen, etc.) aus einfacheren Strukturen zusammengesetzt und letztendlich auf Basisdatentypen (numerische, alphanumerische) zurückgeführt ... dynamisch: können konkrete Ausprägung zur Laufzeit ändern, um zum Beispiel beliebige Anzahl neuer Fakten aufzunehmen oder diese aus der Struktur zu entfernen ... wiederverwendbar: erlauben, wenn einmal definiert, den Einsatz für zahlreiche verschiedene Anwendungen Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 493/543 Datenstrukturen für Kollektionen Kollektionen: Oberbegriff für Datenstrukturen, die eine Sammlung/Anzahl von gleichartigen Objekten verwalten sollen Wichtigste: Mengen, Multimengen, Listen Je nach Anwendung sehr unterschiedliche Anforderungen Duplikate: können (werte-)gleiche Objekte in der Struktur auftreten Ordnung: spielt die Reihenfolge der Elemente in der Struktur eine Rolle Positionaler Zugriff: kann der Zugriff über die Position innerhalb der Struktur erfolgen (vergleichbar Array) Assoziativer Zugriff: kann der Zugriff über einen anderen Wert (Schlüssel) erfolgen Iterativer Zugriff: Durchlaufen aller Elemente in der Kollektion (z.B. mittels Schleife) für alle Strukturen möglich Abgrenzung zum Feld (Array) in Programmiersprachen: Feld ist nicht dynamisch, da feste Anzahl von Elementen Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 494/543 Überblick: Kollektionsdatentypen Kollektionstyp Array / Feld Set / Menge Bag / Multimenge List / Liste Map, Hash Table Eike Schallehn, FIN/ITI Dynamisch nein ja ja ja ja Duplikate ja nein ja ja ja Grundlagen der Informatik für Ingenieure Ordnung ja nein nein ja nein Zugriff Position Position Assoziativ 495/543 Schnittstellen von Kollektionsdatentypen Zum Teil sehr unterschiedlich nach Implementierung Grundlegende Funktionen für alle Kollektionstypen Erzeugen einer (leeren) Kollektion Suchen eines Elementes Einfügen eines Elementes Löschen eines Elementes Spezielle Funktionen für Listen Element an einer bestimmten Position zurückgeben Einfügen eines Elementes am Anfang, am Ende, an einer bestimmten Position Löschen eines Elementes am Anfang, am Ende, an einer bestimmten Position Sortierung der Liste nach einem bestimmten Kriterium Spezielle Funktionen für Maps/Hash-Tabellen Einfügen eines Elementes mit Zugriffsschlüssel Suchen eines Elementes anhand des Schlüssels Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 496/543 Implementierung von Kollektionen /1 Liste Knoten1 4 Knoten2 17 Knoten3 21 KnotenN 37 NULL Grundprinzipien für Mengen, Multimengen, Listen etc. ähnlich 1 2 Verwendung von Klassen oder Strukturen für Kollektions-Schnittstelle sowie innere Knoten Verwendung von Zeigern zum Aufbau der dynamischen Struktur aus einzelnen Knoten Einfachste Lösung: Kollektionsobjekt mit Zeiger auf ersten Knoten Knoten trägt Wert und Zeiger auf nächsten Knoten Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 497/543 Implementierung von Kollektionen /2 Liste Knoten1 4 Knoten2 17 Knoten3 21 KnotenN 37 NULL Zusätzlicher Zeiger auf letztes Element im Listenkopf Erlaubt Einfügen bzw. Löschen des letzten Elementes mit O(1) statt O(n), da Liste nicht erst komplett durchlaufen werden muss Siehe Beispielimplementierung: einfach verkettete Liste in C (→) über Strukturen Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 498/543 Implementierung von Kollektionen /3 Liste NULL Knoten1 4 Knoten2 17 Knoten3 21 KnotenN 37 NULL Häufig verwendete Implementierung: doppelt verkettete Liste (Double Linked List) mit „Rückzeigern“ von jedem Knoten auf seinen Vorgänger Erlaubt Durchlaufen und Navigieren in beliebige Richtung Höherer Speicheraufwand Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 499/543 Implementierung von Kollektionen /4 Zahlreiche Alternativen bei tatsächlichen Implementierung Große Anzahl von Zeigern oft wenig speichereffizient → intern Verwendung von verketteten Arrays Zahlreiche Optimierungen, insbesondere für Suche in Liste (Skip-Listen) Interne Implementierung als Baum (→) oder Hash-Tabelle (→) zur Beschleunigung bestimmter Operationen (zum Beispiel Einfügen in Mengen mit gleichzeitigem Test, ob Element schon in der Menge) ... Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 500/543 Wiederverwendbarkeit für verschiedene Elementtypen Kollektionen werden mit immer wieder gleicher Funktionalität für viele verschiedene Anwendungen benötigt, zum Beispiel Liste von ganzen Zahlen Liste von Vertexes in OpenGL Liste von Studenten (Objekte einer Klasse) ... Bisher: Elementtyp in Knoten-Struktur/Klasse festgelegt Keine Wiederverwendbarkeit: muss für jede Anwendung neu programmiert oder angepasst werden Mögliche Lösungen: void-Pointer (in C) bzw. Templates mit Typparametern in C++ (Java, uva.) Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 501/543 Elemente mittels void* class Node { private: void* element; ... } Erzeugung einzelner Elemente als separate Objekte auf dem Heap und Referenzierung über untypisierten (void) Zeiger Nachteile: Nicht typsicher: Kollektion kann Elemente beliebigen Typs enthalten → fehleranfällig Erfordert prinzipiell Arbeit mit Zeigern Einzige Option in vielen älteren Programmiersprachen, zum Beispiel C Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 502/543 Elemente mittels Templates /1 template <class T> class Node { private : T element; ... }; template <class T> class List { ... }; Typparameter in aktuellen Programmiersprachen (Templates in C++, Generics in Java, Delphi und C#) Erlauben Implementierung generischer „Klassenschablonen“ Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 503/543 Elemente mittels Templates /2 Typparameter werden bei der Erzeugung einer konkreten Variablen durch einen konkreten Typ ersetzt, zum Beispiel List<int> meineListe; Setzt zur Übersetzungszeit T auf int Meist Übersetzung einer separaten Klasse für alle Typparameter Beispiel im folgenden Abschnitt: Warteschlangen (Queues →) mittels Templates In C++: Standard Template Library (STL) setzt wiederverwendbare Kollektionstypen als Templates um → Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 504/543 C++ Standard Template Library (STL) Bietet (vor allem) Kollektionsklassen und einige Standaralgorithmen Kollektionsklassen (Auswahl) list<...>: Liste (geordnet, Duplikate) vector<...>: dynamisches Array, ähnlich Liste set<...>: Menge (ungeordnet, kein Duplikate) multiset<...>: Multimenge (ungeordnet, Duplikate) map<...>: Kollektion mit assoziativem Zugriff (Schlüssel) Iterativer Zugriff (Durchlaufen) von Kollektionen über Iterator-Klassen (→) Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 505/543 C++ STL: Einfaches Beispiel #include <iostream> #include <list> using namespace std; int main() { list<int> zahlenliste; zahlenliste.push_back(7); zahlenliste.push_back(1); zahlenliste.push_back(13); zahlenliste.sort(); list<int>::const_iterator position; for (position = zahlenliste.begin(); position != zahlenliste.end(); position++) cout << *position << ” ”; cout << endl; return 0; } Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 506/543 Iteratoren für Kollektionen Ebenfalls grundlegende Datenstruktur: Hilfsstruktur zum Durchlaufen einer Kollektion Daten bestehen nur aus Verweis (Zeiger, Referenz) auf aktuelle Position (z.B. Knoten) in der Kollektion Methoden und Operatoren zum Steuern des Durchlaufs (Anfang, Ende, Weitersetzen, Zurücksetzen, ...) In C++ STL ebenfalls als Template-Klassen umgesetzt Verschiedene Iteratoren möglich Navigationsrichtungen (vor- und rückwärts, nur vorwärts) Modifikation der Kollektion (z.B. Einfügen, Löschen an Position) erlaubt Wahlfreie Positionierung: beliebiges Setzen der Position, Überspringen von Einträgen, etc. Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 507/543 Zusammenfassung: Kollektionen Verwaltung von Anzahl von Objekten immer wiederkehrendes Problem Unterschiedliche Anforderungen: Duplikate, Ordnung, Zugriffsmöglichkeiten → unterschiedliche Strukturen: Listen, Mengen, Multimengen, Maps → unterschiedliche Implementierungsmöglichkeiten nach Möglichkeiten der Programmiersprache und Anforderungen bzgl. Laufzeit und Speicheraufwand In C++ umgesetzt als Template-Klassen in der STL Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 508/543 Queues und Stacks Beide Datenstrukturen sind Listen (mit eingeschränkter Funktionalität) ähnlich und auch oft vergleichbar implementiert Aber: haben besondere Bedeutung als Zwischenspeicher für die Steuerung der Programmlogik Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 509/543 Queues: Warteschlangen 34 17 45 7 22 13 Enqueue 5 Dequeue FIFO-Prinzip: First In, First Out Entspricht Liste, bei der nur am Anfang geschrieben/eingefügt und am Ende gelesen/entfernt werden kann Zwischenspeicherlösung, welche Daten aufsteigend nach Dauer seit der letzten Bearbeitung bereitstellt: zuerst älteste Daten bearbeiten Zwei wichtige Zugriffsoperationen: enqueue: Einreihen eines Elementes in die Warteschlange dequeue: Auslesen eines Elementes aus der Warteschlange Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 510/543 Verwendung von Queues Meist auf Ebene des Betriebsystems oder von Protokollen Synchronisation (Herstellung einer zeitlichen Reihenfolge) von parallelen Zugriffen auf beschränkte Ressourcen Prozesse auf Prozessoren Lese-/Schreibanforderungen auf Festplatten Transfer von Daten in Netzwerken Druckaufträge an einen Drucker Transaktionen in Datenbanksystemen (→) ... Asynchrone Kommunikation: Zwischenspeicherung eingehender Nachrichten/Daten, z.B. Pipe bei Prozeßkommunikation Simulation von Produktions- und Transportprozessen Lastverteilung auf parallel arbeitende Ressourcen über Kontrolle von Warteschlangen, z.B. Prozessoren in Multiprozessormaschinen oder einzelnen Servern in Server Clustern Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 511/543 Stacks: Stapelspeicher LIFO-Prinzip: Last In, First Out Entspricht Liste, bei der nur am Anfang geschrieben/eingefügt und ebenda gelesen/entfernt werden kann Pop Push Zwischenspeicherlösung, welche Daten absteigend nach Dauer seit der letzten Bearbeitung bereitstellt: zuerst aktuellste Daten bearbeiten Zwei wichtige Zugriffsoperationen: push: Ablegen eines Elementes auf dem Stapel pop: Entnehmen eines Elementes vom Stapel Eike Schallehn, FIN/ITI 17 34 Grundlagen der Informatik für Ingenieure 47 3 22 5 512/543 Verwendung von Stacks Meist auf Ebene der Speicherverwaltung für Programme Mikroprozessoren unterstützen Stapelspeicher direkt: haben Stack Pointer-Register (Zeiger auf oberstes Stack-Element) und Maschinensprache umfaßt Befehle PUSH und POP Programm-Stack: bei Aufruf von Funktionen oder Sub-Routinen werden aktuelle Daten (Variablen, Programmzustand) auf einem Stack verwaltet Rahmen für Daten eines Funktionsaufrufs: Stack Frame Sequentielle Folge aller Funktionsaufrufe (Stack Frames): Stack Trace Syntaktische Analyse von Ausdrücken oder Sätzen (mit implizit aus Regeln gebildeter hierarchischer Struktur) Parser als Teil von Compilern und Interpretern zur Übersetzung von Programmtext Auswertung algebraischer Terme ... Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 513/543 Queue Implementierung Implementierung einer einfachen Queue mit Basisfunktionalität in C++ Verwendet Templates: ermöglicht Wiederverwendung der Queue für verschiedene Elementtypen Queue<int> wi; Queue<char*> wc; Queue<Student> ws; Queue<Student*> wsp; ... Implementierung illustriert auch Grundprinzipien für Kollektions-Datenstrukturen in C++ (einfach verkettete Liste) Vollständiger Quelltext auf der Web-Seite zur Vorlesung Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 514/543 Queue Implementierung: Knoten-Klasse (C++) template <class T> class Node { public : Node(T e, Node<T>* n) { element = e; next = n; } void set_next(Node<T>* n) {next = n;} Node<T>* get_next() { return next;} T get_element() { return element;} private : T element; Node<T>* next; }; Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 515/543 Queue Implementierung: Queue-Klasse (C++) template <class T> class Queue { public : Queue() { first = NULL; last = NULL; } void enqueue(T element); T dequeue(); bool is_empty() { return (first == NULL); } private : Node<T>* first; Node<T>* last; }; Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 516/543 Queue Implementierung: enqueue() (C++) template <class T> void Queue<T>::enqueue(T element) { Node<T>* old_last = last; last = new Node<T>(element, NULL); if (old_last == NULL) first = last; else old_last->set_next(last); } Einfügen eines Elementes durch Erzeugen eines neuen Knotens am Ende der Warteschlange Spezialfall: Warteschlange war vorher leer Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 517/543 Queue Implementierung: dequeue() (C++) template <class T> T Queue<T>::dequeue() { if (first==NULL) throw ”Dequeue from empty queue.”; T e = first->get_element(); Node<T>* old_first = first; first = first->get_next(); if (first == NULL) last==NULL; delete old_first; return e; } Rückgabe des Elementes im Knoten am Listenanfang und dann Knoten löschen Spezialfall: Warteschlange ist danach leer Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 518/543 Queue Implementierung: main() (C++) int main() { Queue<int> w; w.enqueue(19); w.enqueue(1); w.enqueue(42); w.enqueue(13); while (! w.is_empty()) cout << w.dequeue() << ” ”; cout << endl; return 0; } Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 519/543 Zusammenfassung: Queues und Stacks Queue (Warteschlange) und Stack (Stapel) sind listenähnliche Datenstrukturen Besondere Bedeutung für Steuerung von Programmabläufen Grundprinzipien: Queue: „Daten, die ich jetzt nicht bearbeiten kann, packe ich in eine Warteschlange und arbeite diese dann später systematisch ab“ Stack: „Ich bearbeite erstmal die aktuellsten Daten, und packe diese bei noch dringenderen Aufgaben auf den Stapel, von wo ich sie hole, sobald ich mit der aktuellen Aufgabe fertig bin“ Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 520/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 521/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 522/543 Allgemeine Struktur von Bäumen Höhe des Baumes Wurzel Innere Knoten 1 2 3 4 Blätter Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 523/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 524/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 525/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 526/543 Balancierte Binäre Suchbäume: Aufwand Eike Schallehn, FIN/ITI 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 527/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 528/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 529/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 530/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 531/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 532/543 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, FIN/ITI Entarteter Baum bei Einfügereihenfolge 1, 2, 3, 4, 5, 6, 7 Grundlagen der Informatik für Ingenieure 7 533/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 534/543 ... 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, FIN/ITI Grundlagen der Informatik für Ingenieure 535/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 536/543 Einfügen in Hash-Tabellen Zu speichernde Objekte Hash-Tabelle Student Udo Urban MatrNr 170480 0 1 2 Student Eva Lange MatrNr 175783 156324, Max Müller 170480, Udo Urban 3 4 Student Max Müller MatrNr 156324 5 6 175783, Eva Lange Hash-Funktion h(MatrNr)=MatrNr % 7 Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 537/543 Suchen in Hash-Tabellen Hash-Tabelle 0 Suche nach Matrikelnummer: 1 170480 3 2 156324, Max Müller 170480, Udo Urban 4 5 6 175783, Eva Lange Hash-Funktion h(MatrNr)=MatrNr % 7 Eike Schallehn, FIN/ITI Grundlagen der Informatik für Ingenieure 538/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 539/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 540/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 541/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 542/543 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, FIN/ITI Grundlagen der Informatik für Ingenieure 543/543