Datenstrukturen: Mathematische Grundlagen 26. Juli 2015 1 / 27
Asymptotik Die Groß-Oh Notation: f = O(g) Es gibt eine positive Konstante c > 0 und eine natürliche Zahl n 0 N, so dass f (n) c g(n) für alle n n 0 gilt: f wächst höchstens so schnell wie g. f = Ω(g) g = O(f ) : f wächst mindestens so schnell wie g. f = Θ(g)? = O(??) und? = O(??) : f und g wachsen gleich schnell. Die Klein-Oh Notation: f = o(g) lim n?(n)??(n) = 0: f wächst langsamer als g. 26. Juli 2015 2 / 27
Die geometrische Reihe und die Summe der kten Potenzen Die geometrische Reihe: Für a 1 gilt k i=0 ai = ak+1 1 a 1. k sei eine natürliche Zahl. Wie stark wächst die Summe n S k (n) = i k? Sk (n) = n i=1 ik n i=1 nk = n k+1. Also ist S k (n) = O(n k+1 ). i=1 S k (n) = n i=1 ik n i=n/2 ik n i=n/2 (n/2)k (n/2) k+1. Also ist S k (n) = Ω(n k+1 ). Für jede natürliche Zahl k gilt S k (n) = n i k = Θ(n k+1 ) i=1 26. Juli 2015 3 / 27
Asymptotik und Grenzwerte Der Grenzwert der Folge f (n) g(n) möge existieren und es sei f (n) lim n g(n) = c. 1 Wenn c = 0, dann ist f = o(g). 2 Wenn 0 < c <, dann ist f = Θ(g). 3 Wenn 0 c <, dann ist f = O(g) und g = Ω(f ). Wenn wir Grenzwerte beherrschen, dann beherrschen wir die asymptotische Notation (fast). 26. Juli 2015 4 / 27
Der Logarithmus Es gelte a, b > 1 und x sei eine reelle Zahl. Dann ist log a x = y a y = x. 1 log a (x y) = log a? + log a??. 2 log a (x y ) = y log a (x). 3 a log a x = x. 4 log a x = (log a?) (log b??). b log a x = x?, loga n = Θ(log b n): Die Basis wird das Wachstum des Logarithmus nicht beeinflussen, wenn sie nicht von n abhängt. 5 Für jede noch so kleine Potenz ɛ > 0 ist log a n = o(n ɛ ). Der Logarithmus ist eine sehr, sehr langsam wachsende Funktion. 26. Juli 2015 5 / 27
Das Lösen von Rekursionsgleichungen (1/3) Für ein rekursives Programm ist die Rekursionsgleichung ( n ) T (1) = c, T (n) = a T + f (n) b für eine Potenz n der natürlichen Zahl b > 1 zu lösen. Ganz, ganz wichtig: Was entspricht den Parametern a, b and was entspricht der Funktion f (n) in einem rekursiven Programm? Was ist überhaupt die Rolle von n? 26. Juli 2015 6 / 27
Das Lösen von Rekursionsgleichungen (2/3) Die Rekursion T (1) = c, T (n) = a T ( n b ) + f (n) ist für eine Potenz n der natürlichen Zahl b > 1 zu lösen. Dann gilt für a 1, c > 0: 1 Wenn f (n) = O ( n (log b a) ε) für eine positive Konstante ε > 0, dann ist T (n) = Θ(n log b a ). 2 Wenn f (n) = Θ(n log b a ), dann ist T (n) = Θ(n log b a log b n). 3 Wenn f (n) = Ω ( n (log a)+ε) b für eine Konstante ε > 0 und a f ( ) n b αf (n) für eine Konstante α < 1, dann ist T (n) = Θ(f (n)). Vergleiche stets den Overhead f (n) mit a log b n = n log b a. Warum ist das nicht überraschend? Was entspricht der Zahl a log b n? 26. Juli 2015 7 / 27
Das Lösen von Rekursionsgleichungen (3/3) Wenn die Rekursionsgleichung nicht vom obigen Typ ist, wie etwa die Rekursionsgleichung T (1) = 1, T (n) = 2 T (n 1) + 1 für die Anzahl der Züge im Türme von Hanoi Spiel. Dann: a) Expandiere die Rekursion mehrmals, um eine Vermutung über die allgemeine Form der expandierten Rekursion zu erhalten. b) Eliminiere die T-Terme und zuletzt c) bestimme die asymptotische Lösung. Nicht das Mastertheorem ist zentral, sondern das Verständnis des obigen Lösungsprinzips. Übrigens, was ist die asymptotische Lösung der Rekursionsgleichung T (1) = 1, T (n) = T (n 1) + n 2? 26. Juli 2015 8 / 27
Keller, Schlangen und Listen 26. Juli 2015 9 / 27
Listen Listen unterstützen die Operationen Lookup, Insert, Remove. + Listen passen sich der Größe der zu speichernden Datenmenge dynamisch an. - Achtung: Lookup wird durch die lineare Suche implementiert und benötigt eine Laufzeit proportional zur Länge der Liste! Fürchterlich?!? Gleiches gilt für die Insert-Operation (wenn die Schlüssel sortiert abgespeichert werden) wie auch für die Remove-Operation. Die Adjazenzliste für Graphen ist eine wichtige Anwendung. Viele Graph-Algorithmen (wie etwa Tiefen- oder Breitensuche) müssen alle Nachbarn besuchen: Die Abspeicherung der Nachbarn in einer Liste ist optimal! 26. Juli 2015 10 / 27
Keller und Schlangen Keller unterstützen die Operationen Insert und Remove (des jüngsten Schlüssels). + Beide Operationen gelingen in Konstantzeit! + Eine wichtige Anwendung ist die Implementierung der Rekursion. Schlangen modellieren Warteschlangen und unterstützen die Operationen Insert und Remove (des ältesten Schlüssels). + Beide Operationen gelingen in Konstantzeit! + Breitensuche wird mit Schlangen implementiert. 26. Juli 2015 11 / 27
Prioritätswarteschlangen 26. Juli 2015 12 / 27
Insert und Delete_Max Prioritätswarteschlangen unterstützen Warteschlangen mit Prioritäten, nämlich die Operationen Insert und Delete_Max. + Ein Heap implementiert beide Operationen in logarithmischer Zeit. - Lookup ist extrem langsam! + Eine wichtige Anwendung ist Dijkstra s Algorithmus für das Single- Source-Shortest-Path Problem. 26. Juli 2015 13 / 27
Heaps: Nutzen und Fluch der Heapordnung Ein Heap wird durch Heapstruktur und Heapordnung definiert. Was bedeuten diese Begriffe? Die Funktionen Repair_up und Repair_down stellen die Heapordnung wieder her. Was genau tun diese Funktionen? Ein Heap implementiert eine Prioritätswarteschlange durch ein Array: Wie navigiert man im Heap-Array? Wo findet man den Elternknoten, wo die Kinder, wo die Wurzel? Warum ist die Lookup-Operation so schwierig für Heaps, warum sind Insert und Delete_Max einfach? 26. Juli 2015 14 / 27
Bäume 26. Juli 2015 15 / 27
Die wichtigen Begriffe 1 Wie definiert man ungerichtete Bäume und wie gerichtete Bäume und wozu benutzt man eigentlich Bäume? Zur Veranschaulichung (Rekursionsbäume, Verzeichnisbäume, Vorfahrenbäume,...) Zur Implementierung von Wörterbüchern, für die Kompilierung,.... 2 Was ist die Tiefe und was ist die Höhe eines Baums? Und wie berechnet man Tiefe und Höhe? 3 Was ist ein Präorder-, Inorder oder Postorder-Durchlauf? Und wie führt man diese Durchläufe aus? 4 Wie implementiert man Bäume? Man verwendet ein Eltern-Array, die Kind-Geschwister- oder, falls anwendbar, die Binärbaum-Darstellung. Was sind die Vor- und Nachteile der einzelnen Implementierungen? 26. Juli 2015 16 / 27
Präorder void praeorder (Knoten *p) { Knoten *q; if??????? { cout «p wert; for (q=p LKind; q!= null; q=q RGeschwister)???????? 1 Und wie implementiert man Präorder nicht-rekursiv? 2 Wie implementiert man Postorder? 26. Juli 2015 17 / 27
Welcher Knoten wird direkt nach v besucht? Präorder: Wenn v kein Blatt ist:?????????? Wenn v ein Blatt ist:????????? Postorder: Wenn v einen rechten Geschwisterknoten besitzt:???????? Wenn v keinen rechten Geschwisterknoten besitzt:????????? 26. Juli 2015 18 / 27
Graphen 26. Juli 2015 19 / 27
Die wichtigen Begriffe und Methoden Ungerichtete und gerichtete Graphen modellieren Verkehrsnetze, Netzwerke, Schaltungen, den Webgraphen... Für viele Anwendungen ist die Graphdarstellung durch Adjazenzlisten maßgeschneidert. Warum? Begriffe wie Zusammenhangskomponenten, Wege, Distanz zwischen Knoten und azyklische Graphen sind zentral. Wie berechnet man Zusammenhangskomponenten, wie stellt man fest, ob ein Graph azyklisch ist, und wie führt man eine topologische Sortierung durch? Tiefen- und Breitensuche sind wichtige Methoden. Und wie funktionieren diese Algorithmen? 26. Juli 2015 20 / 27
Tiefen- und Breitensuche Welche Kantentypen erzeugt Tiefensuche für ungerichtete Graphen und welche Kantentypen für gerichtete Graphen? Warum interessiert man sich denn überhaupt für Kantentypen und was kann man mit Tiefensuche so alles anstellen? Wie wird der Wald der Tiefensuche berechnet? Wann ist eine Kante eine Baum-, Vorwärts-, Rückwärts- oder Querkante? Wie wird der Baum der Breitensuche berechnet? Warum ist dieser Baum denn wichtig? 26. Juli 2015 21 / 27
Eine automatische Erkennung der Kantentypen Wir benutzen zwei integer-arrays Anfang und Ende als Uhren, um den Zeitpunkt des Beginns und des Endes des Besuchs festzuhalten. Anfangnr=Endenr=0; void tsuche(int v) {Knoten *p; Anfang[v] = ++Anfangnr; for (p = A[v]; p!= 0; p = p->next) if (!Anfang[p->name]) tsuche(p->name); Ende[v] = ++Endenr; } - e = (u, v) ist eine Vorwärtskante Anfang [u]? Anfang [v] und e = (u, v) ist keine Baumkante. - e = (u, v) ist eine Rückwärtskante Anfang [u]? Anfang [v] und Ende [u]? Ende [v]. - e = (u, v) ist eine Querkante Anfang [u]? Anfang [v] und Ende [u]? Ende [v]. 26. Juli 2015 22 / 27
Wörterbücher 26. Juli 2015 23 / 27
Was muss ich wissen? 1. Welche Operationen muss ein Wörterbuch unterstützen? 2. Welche wichtigen Datenstrukturen implementieren Wörterbücher? (a) Binäre Suchbäume (b) AVL Bäume (c) (a, b)-bäume (d) Hashing mit Verkettung mit offener Adressierung Welche Datenstruktur wählt ein Angsthase? ein Profi mit einigermaßen guten Nerven? ein Profi bei extern gespeicherten Daten? ein Chaot? 26. Juli 2015 24 / 27
Hands on Wie funktioniert (a) ein binärer Suchbaum unter Lookup-, Insert- und Remove-Operationen? (b) ein AVL-Baum unter Lookup- und Insert-Operationen? (c) ein (a, b)-baum unter Lookup-, Insert- und Remove-Operationen? (d) Hashing mit Verkettung, bzw. Hashing mit offener Adressierung unter Lookup-, Insert- und Remove-Operationen? 26. Juli 2015 25 / 27
Was war das nochmal? (a) AVL-Bäume: Was sind Rotationen? Bei der Insert-Operation traten vier Fälle auf. Welche? (b) (a, b)-bäume: Wenn ein Knoten in der Insert-Operation zu viele Schlüssel besitzt, dann... Wenn ein Knoten in der Remove-Operation zu wenige Schlüssel besitzt, dann... Wie groß ist die Tiefe mindestens und höchstens in Abhängigkeit von n, a und b? Achtung: a und b sind keine Konstanten. (c) Hashing: Welche Hashfunktion wählt man in Hashing mit Verkettung? Wie funktioniert das lineare Austesten und das doppelte Hashing? Wie ist der Auslastungfaktor definiert? Wie lange dauert eine erfolglose Suche für Hashing mit Verkettung, bzw. Hashing mit offener Adressierung? 26. Juli 2015 26 / 27
Unser Werkzeugkasten (a) Listen (b) Stacks (c) Queues, (d) Bäume und Graphen (e) Heaps, (f) Binäre Suchbäume, (g) AVL-Bäume, (h) (a, b)-bäume, (i) Hashing. Welches Werkzeug für welche Aufgabe? Für neue Aufgabenstellungen muss ich neue Datenstrukturen aus alten bauen! 26. Juli 2015 27 / 27