Zusammenfassung Datenstrukturen & Algorithmen M. Leonard Haufs Sommersemester 2012 Wichtige Hinweise: Letzte Bearbeitung: Sonntag, 9. Dezember 2012 Ihr dürft die Zusammenfassung selbstverständlich gerne nutzen. Ich habe nur eine Bedingung. Wenn ihr Fehler im Dokument findet, schreibt sie mir bitte an folgende Adresse, damit ich die Zusammenfassung verbessern kann: [email protected] Viele der Abbildungen dieser Zusammenfassung sind folgender Vorlesung entnommen: Datenstrukturen und Algorithmen Prof. Dr. Joost-Pieter Katoen (RWTH Aachen, Sommersemester 2012) Inhaltsverzeichnis 1 Komplexitätsklassen 1 2 Rekursionsgleichungen 1 Rekursionsbäume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Substitutionsmethode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Mastertheorem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 3 4 3 Datenstrukturen 1 Listen . . . . . . . . . . . . 2 Bäume . . . . . . . . . . . . 2.1 Binärbäume . . . . . 2.2 Binäre Suchbäume . 2.3 Rot-Schwarz-Bäume 3 Graphen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 6 6 7 9 11 4 Flüsse 1 Allgemeine Flusseigenschaften . . . . . 1.1 Flussnetzwerke . . . . . . . . . 1.2 Fluss in einem Flussnetzwerk . 1.3 Flüsse zwischen Knotenmengen 2 Ford-Fulkerson-Methode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 15 15 15 16 16 . . . . . 17 17 17 17 18 19 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Algorithmenprinzipien 1 Divide & conquer - Teilen und Beherrschen . 2 Greedy-Algorithmen . . . . . . . . . . . . . 3 Dynamische Programmierung . . . . . . . . 3.1 Rucksackproblem . . . . . . . . . . . 3.2 Longest Common Subsequence (LCS) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Sortieralgorithmen 21 1 Einfache Sortieralgorithmen . . . . . . . . . . . . . . . . . . . . . . . . . 21 1.1 Insertionsort - Sortieren durch einfügen . . . . . . . . . . . . . . . 21 1.2 Selectionsort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 i Inhaltsverzeichnis 2 3 Höhere Sortieralgorithmen . . . . . . . . 2.1 Heapsort . . . . . . . . . . . . . . 2.2 Countingsort . . . . . . . . . . . 2.3 Mergesort . . . . . . . . . . . . . 2.4 Quicksort . . . . . . . . . . . . . 2.5 Dutch National Flag- Problem . . Gesamtübersicht über Sortieralgorithmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Graphenalgorithmen 1 Graphensuche . . . . . . . . . . . . . . . . . . . . . 1.1 Tiefensuche . . . . . . . . . . . . . . . . . . 1.2 Breitensuche . . . . . . . . . . . . . . . . . . 2 Sharir’s Algorithmus . . . . . . . . . . . . . . . . . 3 Prims Algorithmus . . . . . . . . . . . . . . . . . . 4 Single Source Shortest Path (SSSP) . . . . . . . . . 4.1 Dijkstra-Algorithmus . . . . . . . . . . . . . 4.2 Bellman-Ford-Algorithmus . . . . . . . . . . 5 Topologische Sortierung . . . . . . . . . . . . . . . 6 All Pairs Shortest Paths . . . . . . . . . . . . . . . 6.1 Floyd-Warshall Algorithmus . . . . . . . . . 7 Algorithmen auf Flussnetzwerken . . . . . . . . . . 7.1 Ford-Fulkerson-Methode - Maximaler Fluss . 7.2 Min-cut-max-flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 22 24 25 26 27 29 . . . . . . . . . . . . . . 30 30 30 32 33 34 35 35 36 37 39 39 40 40 41 8 Hashing 42 1 Hashfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 2 Geschlossenes Hashing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 3 Offenes Hashing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 9 Algorithmische Geometrie 47 1 Mathematische Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . 47 2 Graham-Scan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 ii 1 Komplexitätsklassen O(f ) g ∈ O(f ) ⇔ ∃c > 0, n0 : ∀n ≥ n0 : 0 ≤ g(n) ≤ c · f (n) Alternative Definition: g ∈ O(f ) ⇔ lim sup n→∞ g(n) = c ≥ 0, c 6= ∞ f (n) Ω(f ) g ∈ Ω(f ) ⇔ ∃c > 0, n0 : ∀n ≥ n0 : c · f (n) ≤ g(n) Alternative Definition: g ∈ Ω(f ) ⇔ lim inf n→∞ g(n) =c>0 f (n) Θ(f ) g ∈ Θ(f ) ⇔ ∃c1 , c2 > 0, n0 : ∀n ≥ n0 : c1 · f (n) ≤ g(n) ≤ c2 · f (n) Alternative Definition: g(n) = c, 0 < c < ∞ n→∞ f (n) g ∈ Θ(f ) ⇔ lim o(f ) g ∈ o(f ) ⇔ ∀c > 0, n0 : ∀n ≥ n0 : 0 ≤ g(n) ≤ c < f (n) ω(f ) g ∈ ω(f ) ⇔ ∀c > 0, n0 : ∀n ≥ n0 : f (n) < g(n) 1 a) n T (n) = 23T (b 23 c) + 42n. Die Koeffizienten des Mastertheorems sind: b = 23, c = 23 und somit ist E = log23 23 = 1 und f (n) = 42n 2 ⇥(nE ). Der 2. Fall des Mastertheorem liefert dann: T (n) 2 ⇥(n· log n) b) T (n) = 9T ( n3 ) + 27n Die Koeffizienten des Mastertheorems sind: b = 9, c = 3 und somit ist E = log3 9 = 2. Da f (n) = 27n 2 2O(n Rekursionsgleichungen ) wenden wir den 1. Fall des Mastertheorem an und erhalten: 2 1 T (n) 2 ⇥(n2 ) c) T (n) = 2T ( n ) + n log n 2 2 1 Rekursionsbäume Es gilt b = 2, c = 2. Somit ist E = log2 2 = 1 und f (n) = n log2 n. Hier ist das Mastertheorem nicht anwendbar da keiner der drei Fälle anwendbar ist: Allgemeines Prinzip: • n log2 n 2 / O(n1 ✏ ) Mithilfe von Rekursionsbäumen lässt sich die Lösung einer Rekursionsgleichung erraten, 1 • n log n 2 / ⇥(n ) 2 indem die Aufteilung in Teilprobleme iterativ dargestellt wird. • n log2 n 2 / ⌦(n1+✏ ) Beispiel: 2T ( n2 ) + n · log T (0)polynomial =1 denn n logT2 (n) n ist=asymptotisch aber nicht größer als n. 2 n, Wir betrachten den Rekursionsbaum: T (n) n · log2 n n log2 n T (n/2) n/2 · log2 n/2 log2 n T (n/9) n/18 + 3 T (n/2) n/2 · log2 n/2 T (n/9) n/18 + 3 T (n/9) n/18 + 3 T (0)T (0)T (0)T (0)T (0)T (0) T (n/9) n/18 + 3 2 · n/2 log2 n/2 4 · n/4 log2 n/4 T (0)T (0)T (0)T (0)T (0)T (0) 2log2 n = n aus dem Rekursionsbaum lesen wir eine Komplexität von: log2 n log2 n nlog 2 n n log2 n X X Xn i n n X X log n+1 ii 2 2 + log i T (n) = 2 · · log + 2 = 2 · log 2 · 2nn · (log2 n 2 T (n) = (2 · i log22i i ) =2n ·(2i log2 n log2 2 ) = n(log n i 2 2 2 2 i=02 i=0 log2 n i=0 i=0 i=0 1) ) 2 ⇥(n ·log22 n) Die Rekursionsgleichung leitet sich wie folgt her: Wir nutzen die Substitutionsmethode um die Vermutung zu beweisen: h X (Rekursive Kosten auf Ebene i) + Gesamtkosten der Blätter(= c · bh+1 ) i=0 6 mit Verästelungsgrad b. Bei der Bestimmung der Höhe h ist folgendes zu beachten: - Ist der Basisfall T(0), so ist die Höhe um 1 erhöht, da zusätzlich die Elemente der Ebene T(0) betrachtet werden. 2 KAPITEL 2. REKURSIONSGLEICHUNGEN Typische Höhen: - T (n) = b · T ( nc ) → h = logc n - T (n) = b · T (n − c), c ∈ {1, 2} → h= n c √ - T (n) = b · T ( n) → h = log2 log2 n 2 Substitutionsmethode Allgemeines Vorgehen: - Lösung ’raten’ (z.B. Rekursionsbaum) - Betrachte die jeweilige Definition des betrachteten Falls (O, Ω,, Θ, o, ω) - Setze die geratene Lösung in den rekursiven Teil (T (n)) der Rekursionsgleichung ein und beweise entsprechend der Definition des betrachteten Falls. - Teile durch Umformen den Term in 2 Teile auf: - Einem Teil, der einer Konstanten multipliziert mit der beweisenden Schranke entspricht und - Einem Restterm, durch Abschätzen über die Konstante die Definition des Falls bestärkt. - Gegebenenfalls muss durch Variablentransformation die Rekursionsgleichung zunächst vereinfacht werden. - Häufigste Transformationen: - n = 2m ⇔ m = log2 n - T (2m ) = S(m) Beispiel: Bestimme die exakte Lösung der Rekursionsgleichung √ T (n) = 3 n + 3 mit T (2) = 2. 3 , T (n) = 16n 3 + 12n 3 ) + 4n + 3 2 ,T (n) 3 2 = 48n KAPITEL 2. REKURSIONSGLEICHUNGEN q.e.d. zu b) p T (n) = T ( 3 n) + 3 m 3 , T (2m ) = T (2 ) + 3 m S(m) = S( ) + 3 3 S(m) = 3 · log3 m + S(1) , T (n) = 3 · log3 log2 n + 2 , , , S(m) = 3 · log3 m + 2 |Variablentransformation m = log2 n | Umbenennung T (2m ) = S(m) | Lösung bekannter Rekursionsgleichung: S(m) = S( | da S(1) = T (2) = 2 (Anfangsbedingung) | m = log2 n 2 3 Mastertheorem n T (n) = b · T ( ) + f (n) c mit b ≤ 1 und c > 1 Anzahl der Blätter im Rekursionsbaum: nE mit E = log(b) log(c) = logc b 1. f (n) ∈ O(nE− ) (für ein > 0) → T (n) ∈ Θ(nE ) 2. f (n) ∈ Θ(nE ) → T (n) ∈ Θ(nE · log(n)) 3. f (n) ∈ Ω(nE+ ) (für ein > 0) und b · f ( nc ) ≤ d · f (n) (für d < 1, n hinreichend groß) → T (n) ∈ Θ(f (n)) Grenzen des Mastertheorems: Das Mastertheoren ist nicht anwendbar, wenn - die Funktion f (n) negativ ist. - b oder c ∈ / N>0 . - der Unterschied von nE und f (n) nicht polynomiell ist. 4 m )+3 3 14 I In (Forts.) Vorlesung 8 (Heapsort) werden wir eine weitere Implementierung Liste kennenlernen. I Element minimum(List l) gibt das Element mit dem kleinsten Schlüssel zurück. ú 2 3 4 Beinhaltet das Verschieben aller Elemente „rechts“ von k. Element maximum(List l) gibt das Element mit dem höchsten Mittels binärer Suche. †I Schlüssel zurück. Joost-Pieter Katoen 7 Datenstrukturen und Algorithmen Element successor(List l, Element x) gibt das Nachfolgerelement von Element x zurück. Elementare Datenstrukturen Verkettete Listen 21/39 Element predecessor(List l, Element x) gibt das Einfach verkettete Vorgängerelement vonListen Element x zurück. 3 Listen l.head( 13 14 Datenstrukturen Datenstrukturen und Algorithmen 25/39 Eine einfach verkettete Liste ist eine rekursive, dynamische Datenstruktur. Joost-Pieter Katoen bestehend aus Schlüssel k sowie Zeiger auf ein 1 Listen Elemente nachfolgendes Element I Elementare Datenstrukturen Verkettete Listen Liste. 1 void reverse(List l) { Doppelt verkettete Liste Eleme Operation überg insert(L, gibt. I last = l.head; next 3 head pos = last.next; 48 34 sowohl vorwärts 2 als auch rückwärts 25 Eine doppelt verkettete Liste kann 4 last.next = null; durchlaufen werden. Sie implementiert den ADT Liste. 2 pos tmp last while(posbesitzen != null){ Elemente neben Schlüssel und Nachfolgender einen weiteren 7 tmp auf = pos.next; Zeiger das vorherige Element. verkettete Listen I 6 Doppelt chfolgerelement pos.next = last; 8 last = pos; Zusätzlicher Zeiger tail zeigt auf das letzte Element der Liste I 9 10 pos = tmp; Joost-Pieter Katoen Datenstrukturen und Algorithmen 11 } 13 l.head = last; 14 } 48 next prev 34 2 23/39 25 head 25/39 Datenstrukturen und Algorithmen Joost-Pieter Katoen Laufzeiten 27/39 Verkettete Listen Laufzeiten Implementierung Operation einen weiteren der Liste 25 insert(L,x) remove(L,x) search(L,k) minimum(L) maximum(L) search(L,k) minimum(L) maximum(L) successor(L,x) predecessor(L,x) tail I 27/39 einfach verkette Liste doppelt verkettete Liste ⇥(1) ⇥(n) ⇥(n) ⇥(n) ⇥(n) ⇥(n) ⇥(n) ⇥(n) ⇥(1) ⇥(n) ⇥(1) ⇥(1) ⇥(n) ⇥(n) ⇥(n) ⇥(n) ⇥(n) ⇥(n) ⇥(1) ⇥(1) Suchen eines Schlüssels erfordert einen Durchlauf der gesamten Liste. Gibt es andere Möglichkeiten, die Daten zu organisieren? Joost-Pieter Katoen Datenstrukturen und Algorithmen 5 I 26/39 Datenstrukturen und Algorithmen Elementare Datenstrukturen h rückwärts remove(L, search(L, minimum(L maximum(L Joost-Pieter Katoe search(L, minimum(L maximum(L successor predecess tail Joost-Pieter Katoen der L Laufzeite I void head zeigt auf das erste Element der Liste höchsten void Elementare Datenstruk I Listen können dynamisch erweitert werden verkettete Listen Einfach Doppelt verkettete Listen I kleinsten Eine Liste Reihenfolg Joost-Pieter Katoen I Verkettete Listen Listen umdrehen } Liste Einfach verkettete Liste Elementare Datenstrukturen while( tmp = Joost-Pieter Katoe 8 pos.n 9 last 10 pos = Elementare 11 } Datens 6 I I void rev Binäre last Trav= pos = last.n 28/39 Suchen Gibt es Joost-Pieter Katoen Vater/Mutter von B und C Linkes Kind von A 2 Bäume A Schlüssel 12 KAPITEL 3. t DATENSTRUKTUREN ri gh f t le Rechtes Kind B von A 6 C 225 2.1 Binärbäume 29/39 Joost-Pieter Katoen Allgemeiner Aufbau Datenstrukturen und Algorithmen 30/39 Ein Binärbaum ist ein gerichteter, zykelfreierBinäre Graph mit Knoten (nodes, allgemein: verElementare Datenstrukturen Bäume tices) V und gerichteten Kanten (edges). Binärbäume – Begriffe (I) Wurzel h (V , E ) ten (edges) oot). s 2. el. Höhe des Binärbaums = 3 102 innerer Knoten 6 34 23 Ebene 0 225 103 Ebene 1 1056 40 Ebene 2 Ebene 3 Blatt 31/39 Joost-Pieter Katoen Datenstrukturen und Algorithmen Begriffsklärung Binärbäume 32/39 - Es gibt genau einen ausgezeichneten Knoten, die Wurzel (root). - Alle Kanten zeigen von der Wurzel weg. - Diese Kanten sind Zeiger. Jedes Element bekommt zwei dieser Zeiger (left und right) zu den nachfolgenden Elementen. - Der Ausgangsgrad jedes Knotens ist höchstens 2. - Der Eingangsgrad eines Knoten ist 1, bzw. 0 (bei der Wurzel). - Sonderfall: Baum mit V = E = ∅. - Ein Knoten mit leerem linken und rechten Teilbaum heißt Blatt (leaf). - Die Tiefe (auch: Ebene) eines Knotens ist sein Abstand, d. h. die Pfadlänge, von der Wurzel. - Die Höhe eines Baumes ist die maximale Tiefe seiner Blätter. 6 KAPITEL 3. DATENSTRUKTUREN Allgemeine Eigenschaften - Ebene d hat höchstens 2d Knoten. - Mit Höhe h kann ein Binärbaum maximal 2h+1 − 1 Knoten enthalten. - Mit n Knoten hat ein Binärbaum mindestens die Höhe h = dlog2 n + 1e − 1 Traversierungen Eine Traversierung ist ein Baumdurchlauf mit folgenden Eigenschaften: 1. Die Traversierung beginnt und endet an der Wurzel. 2. Die Traversierung folgt den Kanten des Baumes. Jede Kante wird genau zweimal durchlaufen: Einmal von oben nach unten und danach von unten nach oben. 3. Die Teilbäume eines Knotens werden in festgelegter Reihenfolge (zuerst linker, dann rechter Teilbaum) besucht. 4. Unterschiede bestehen darin, bei welchem Durchlauf man den Knoten selbst (bzw. das dort gespeicherte Element) „besucht“. (Die Arraydarstellung einer Traversierung nennt man Linearisierung) Es gibt 3 verschiedene Formen der Traversierung: - Inorder-Traversierung: “Scannen von links nach rechts” Zuerst linken Teilbaum, dann Wurzel, dann rechten Teilbaum ausgeben. - Preorder-Traversierung: “Wie Einfügereihenfolge” Zuerst Wurzel, dann linken, dann rechten Teilbaum ausgeben. - Postorder: “Soweit wie möglich runter” Zuerst linken, dann rechten Teilbaum, dann Wurzel ausgeben. 2.2 Binäre Suchbäume Allgemeine Definition Ein binärer Suchbaum (BST) ist ein Binärbaum, der Elemente mit Schlüsseln enthält, wobei der Schlüssel jedes Knotens - mindestens so groß ist, wie jeder Schlüssel im linken Teilbaum und - höchstens so groß ist, wie jeder Schlüssel im rechten Teilbaum. Führt man auf einem binären Suchbaum eine Inordertraversierung durch, so erhält man mit einer Laufzeit von Θ(n) die Schlüssel als Linearisierung in sortierter Reihenfolge. 7 KAPITEL 3. DATENSTRUKTUREN Operationen auf Binären Suchbäumen Suchen Traversiere (durchsuche) den Baum: - Falls der gesuchte Schlüssel gleich dem aktuell betrachteten Schlüssel ist: Suche beenden - Falls der gesuchte Schlüssel kleiner als der aktuell betrachtete Schlüssel ist: Durchlaufe den linken Teilbaum (linkes Kind ist neuer betrachtete Schlüssel) - Falls der gesuchte Schlüssel größer als der aktuell betrachtete Schlüssel ist: Durchlaufe den rechten Teilbaum (rechtes Kind ist neuer betrachtete Schlüssel) Komplexität: Θ(h) Einfügen 1. Suche einen geeigneten freien Platz: Wie bei der regulären Suche, außer dass, selbst bei gefundenem Schlüssel, weiter abgestiegen wird, bis ein Knoten ohne entsprechendes Kind erreicht ist. 2. Hänge den neuen Knoten an: Verbinde den neuen Knoten mit dem gefundenen Vaterknoten. Komplexität: Θ(h) (wegen der notwendigen Suche) Nachfolger finden Es gibt 2 mögliche Fälle: 1. Der rechte Teilbaum existiert: → Der Nachfolger ist der linkeste Knoten (das Minimum) im rechten Teilbaum. 2. Der rechte Teilbaum existiert nicht → Der Nachfolger ist der jüngste Vorfahre, dessen linker Teilbaum den Ausgangsknoten enthält. Komplexität: Θ(h) 8 Einfügen von n (unterschiedliche) Schlüssel in zufälliger Reihenfolge in einem anfangs leeren Baum entsteht. 3. DATENSTRUKTUREN Annahme: jede der KAPITEL n! möglichen Einfügungsordnungen hat die gleiche Wahrscheinlichkeit. Löschen Drei mögliche Fälle: Theorem (ohne Beweis) 1. Knoten hat keine Kinder: Lösche den Knoten Die erwartete Höhe eines zufällig erzeugten BSTs mit n Elementen ist 2. Knoten hat ein Kind: Schneide den Knoten aus. Verbinde also den Vater- und den O(log n). Kindknoten direkt miteinander. Fazit: Im Schnitt verhält sich eine binäre Suchbaum wie ein (fast) - Finde den Nachfolger. balancierte Suchbaum. 3. Knoten hat zwei Kinder: - Lösche den Nachfolger aus dem Baum. Joost-Pieter Katoen - Ersetze Datenstrukturen und Algorithmen den Knoten durch dessen Nachfolger. 24/29 Komplexität: Θ(h) Binäre Suchbäume Rotationen Links- und Rechtsrotationen leftRotate – Konzept und Beispiel 2 leftRotate(1) 1 2 A rightRotate(2) C B 1 A C B Beispiel Komplexität: Θ(1) 15 2.3 Rot-Schwarz-Bäume 5 Allgemeine Eigenschaften 3 12 20 10 14 17 31 15 leftRotate(5) 12 20 14 haben, hat die 5 eine Farbe 31 17RotEin binärer Suchbaum, dessen Knoten jeweils zusätzlich Schwarz-Eigenschaft, falls folgende Bedingungen erfüllt sind: 1. Jeder Knoten ist entweder rot oder schwarz. 3 10 13 13 ist schwarz. 2. Die Wurzel 3. Jedes (externe) Joost-Pieter Katoen Datenstrukturen und Algorithmen Blatt ist schwarz. 4. Ein roter Knoten hat nur schwarze Kinder. 5. Für jeden Knoten enthalten alle Pfade, die an diesem Knoten starten und in einem Blatt des Teilbaumes dieses Knotens enden, die gleiche Anzahl schwarzer Knoten. 9 26/29 Einfügen von Schlüssel k in einen RBT – Strategie Einfügen KAPITEL DATENSTRUKTUREN Zum Einfügen in einen RBT3.gehen wir zunächst wie beim BST vor: I Finde einen geeigneten, freien Platz. I Hänge den eines neuenKnotens Knoten an. Die Schwarzhöhe bh(x) x ist die Anzahl schwarzer Knoten bis zu einem (externen)EsBlatt, ausgenommen. Schwarzhöhe bh(t) eines RBT t ist die Schwarzbleibtxdie Frage nach derDie Farbe: Höhe seiner IWurzel. Färben wir den neuen Knoten schwarz, dann verletzen wir in der Regel die Schwarz-Höhen-Bedingung. 1 2 3 4 5 6 ElementareIEigenschaften Färben wir ihn aber rot, dann könnten wir eine Verletzung der 7 Farbbedingungen bekommen (die ist schwarz, rote Knoten Ein Rot-Schwarzbaum t mit Schwarzhöhe h =Wurzel bh(t) hat: Rot-Schwarz haben Bäume keine roten Kinder). Rot-Schwarz-Bäume – Mindestens − 1 den innere Knoten. ∆ Wir 2h färben Knoten rot – ein Schwarz-Höhen-Verletzung wäre schwieriger behandeln. Einfügen in zu einen RBT – Algorithmus ie 8 void r bstI node node node // s rbtI } – Höchstens I4hRot − 1istinnere Knoten. sozusagen eine lokale Eigenschaft, schwarz eine globale. I Behebe daher im letzten Schritt die Farbverletzung. – Ein Rot-Schwarz-Baum mit n inneren Knoten hat höchstens die Höhe 2·log(n+1). Joost-Pieter Katoen Datenstrukturen und Algorithmen 9/31 Joost-Pieter Kat Operationen auf Rot-Schwarzbäumen void Bäume rbtIns(Tree t, Node node) { // Füge node in den Baum t ein Rot-Schwarz-Bäume Einfügen1Rot-Schwarz 2 bstIns(t, node); // Einfügen wie beim BST 3 Einfügen node.left null; – =Was kann passieren? (1) 4 node.right = null; 5 node.color = RED; // eingefügter Knoten immer zunächst rot c c 6 // stelle Rot-Schwarz-Eigenschaft node node ggf. wieder her 7 rbtInsFix(t, node); 8 } n n Einfüg c node c node Der neu eingefügte Knoten ist immer rot. I Ist die Farbe des Vaterknotens c schwarz (z. B. die Wurzel), haben - Jeder neu eingefügte Knoten ist rot. wir kein Problem. Joost-Pieter Katoen Datenstrukturen und Algorithmen Ist c aber rot,c schwarz, dann liegt eine Rot-Rot-Verletzung die wir - Ist derIVaterknoten ist der Einfügevorgang vor, abgeschlossen. behandeln müssen. Rot-Schwarz Bäume Rot-Schwarz-Bäume - Ist derIVaterknoten c rot,lassen so muss Rot-Rot-Verletzung werden. Die unteren Fälle sichdie analog (symmetrisch) zu behoben den oberen Es gibt 3lösen, mögliche daherFälle: betrachten wir nur die beiden oberen Situationen. Einfügen – Was kann passieren? (2) Fall 1: Onkelknoten e ist rot Joost-Pieter Katoen Datenstrukturen und Algorithmen Wir müssen nun Großvater d und Onkel e mit berücksichtigen: d c I c Wir müs Der sich Fall 1 Ist e rot, d auf rot 10/31 11/31 d e Einfüg I I 9/31 Rot-Schwarz Bäu e Der Großvater des eingefügten Knotens war schwarz, denn es handelte sich vor dem Einfügen um einen korrekten RBT. - Umfärben von c und e auf schwarz und d auf rot Fall 1 Ist e rot, dann können wir durch Umfärben von c und e auf schwarz sowie d auf rot die Rot-Schwarz-Eigenschaft lokal wieder herstellen. 10 I Zwei Ebenen weiter oben könnte nun aber eine Rot-Rot-Verletzung vorliegen, die nach dem selben Schema iterativ aufgelöst werden kann. I Ist d allerdings die Wurzel, dann färben wir sie einfach wieder schwarz. Dadurch erhöht sich die Schwarzhöhe des Baumes um 1. I Zwe vorl I Ist d schw Joost-Pieter Kat KAPITEL 3. DATENSTRUKTUREN - Löse evtl. höher liegende Rot-Rot-Verletzung iterativ auf. Rot-Schwarz Bäume Rot-Schwarz-Bäume - ist d die Wurzel, so wird d wieder schwarz gefärbt. Einfügene –ist Was kann (3) Fall 2: Onkelknoten schwarz, neuerpassieren? Knoten liegt innen Ist der Onkel e dagegen schwarz, dann erhalten wir Fall 2 und 3: d d c e c Fall 2 e Fall 3 - Überführe um Vater c nach außen. Fall 2zu Fall 3 durch Rotation Rot-Schwarz-Bäume Fall 3: Onkelknoten e, neuer liegt außen um c auf Fall 3 reduzieren. Dieser Fall lässt sich Knoten durch Linksrotation Rot-Schwarz Bäume Einfügen – Was kann passieren? (4) I d 3: I c x Die Schwarz-Höhe des linken Teilbaumes von d ändert sich dadurch nicht.bh(·) = x + 1 bh(·) = ??? c d Der bisherige Vaterknoten xc wird dabei zum x x + 1linken, roten Kind, das ≠æ eine Rot-Rot-Verletzung mit dem neuen Vater (c im rechten Bild) e d x x x hat, die wir mit Fall 3 beheben können. e Joost-Pieter Katoen Datenstrukturen und Algorithmen 13/31 Fall 3 I Rot-Schwarz Bäume Rot-Schwarz-Bäume Zunächst rotieren wir um d nach rechts, wobei wir die - Rotation in Richtung e um Großvaterknoten d. Schwarz-Höhen im Auge behalten. uzieren. Einfügen – Was kann passieren? (4) ich dadurch - d rot färben. I Um die Schwarz-Höhen der Kinder von c wieder in Einklang zu bringen,färben. färben wir d rot. Da bh(·) dessen linkes Kind ursprünglich am - c schwarz =x +1 roten c hing, ist das soweit unproblematisch. d n Kind, das hten Bild) x Komplexität: Θ(log n) x 13/31 c e x ≠æ Joost-Pieter Katoen Datenstrukturen und Algorithmen Rot-Schwarz Bäume Rot-Schwarz-Bäume 3 Graphen x bh(·) = x + 1 c x d x 14/31 x e Fall 3 Definitionen Einfügen in einen RBT – Algorithmus Teil 2 I Zunächst rotieren wir um d nach rechts, wobei wir die Ein gerichteter Graph G ist ein Paar (V, E) mit Schwarz-Höhen im Auge behalten. - einer Menge Knoten (vertices) V und 1 // Behebe I eventuelle Rot-Rot-Verletzung mit Vater Um die Schwarz-Höhen der Kinder von c wieder in Einklang zu 2 void rbtInsFix(Tree t, Node node) { bringen, färben wir d rot. Da dessen linkes Kind ursprünglich am 3 // solange noch eine Rot-Rot-Verletzung - einer Menge (geordneter) Paare von Knoten E ⊆besteht V xV , die (gerichtete) Kanten roten c hing, ist soweit 4 while (node.parent.color ==das RED) { unproblematisch. (edges)5 genannt werden. == node.parent.parent.left) { if (node.parent I Färben wir nun c schwarz, dann haben wir wieder einen gültigen 6 // der von uns betrachtete Fall - Falls E7 eine Menge ungeordneter Paare ist,des heißt G ungerichtet. RBT. Die Schwarzhöhe Gesamtbaumes ist unverändert. leftAdjust(t, node); =x +1 x d x e // möglicherweise wurde node = node.parent.parent gesetzt 8 0 Joost-Pieter Katoen Graphen G = (V , E ) ist Datenstrukturen 9 (subgraph) } else { eines Ein Teilgraph ein GraphundGAlgorithmen = (V 0 , E 0 ) mit: // der dazu symmetrischer Fall rightAdjust(t, node); 10 11 12 ang zu glich am 13 14 15 } } } t.root.color = BLACK; // Wurzel bleibt schwarz 11 gültigen ert. 14/31 Joost-Pieter Katoen Datenstrukturen und Algorithmen 15/31 14/31 KAPITEL 3. DATENSTRUKTUREN Elementare Graphenalgorithmen I - V 0 ⊆ V und E 0 ⊆ E. 0 Graphen Terminologie bei Graphen (III) 0 – Ist V ⊂ V und E ⊂ E, so heißt G’ echter Teilgraph. A Elementare Graphenalgorithmen I C B Graphen - Transponierter Graph: In GT ist die Richtung der Kanten von G umgedreht. F D Terminologie bei Graphen (III) E Teilgraph A D A B C D E F B A B D E Teilgraph Vollständiger (und symmetrischer) Digraph auf vier Knoten Terminologie bei Graphen Joost-Pieter Katoen Datenstrukturen und Algorithmen Grapheigenschaften E Vollständiger (und symmetrischer) Digraph - Der Graph heißt symmetrisch, wenn aus (v, w) ∈ E folgt (w, v) ∈ E. auf vier G Knoten Joost-Pieter Katoen - Der Graph G ist vollständig, wenn jedes Paar von Knoten mit einer Kante verbunDatenstrukturen und Algorithmen 9/36 den ist. - Knoten w ist adjazent zu Knoten v, wenn (v, w) ∈ E. - Transponiert man G, so erhält man GT = (V, E 0 ) mit (v, w) ∈ E 0 gdw. (w, v) ∈ E. - Ein Weg von Knoten v nach w ist eine Folge von Kanten (vi , vi+1 ) : v0 v1 v2 ...vk−1 vk , so dass v0 = v und vk = w - Ein Weg, bei dem alle Kanten verschieden sind, heißt Pfad. - Länge eines Pfades (Weges) ist die Anzahl der durchlaufenen Kanten. - Knoten w heißt erreichbar von v, wenn es einen Pfad von v nach w gibt. - Ein Zyklus ist ein nicht-leerer Weg bei dem der Startknoten auch Endknoten ist. - Ein Zyklus der Form vv heißt Schleife (loop, self-cycle). - Ein Graph ist azyklisch, wenn er keine Zyklen hat. 12 9/36 Elementare Graphenalgorithmen I Graphen 3. DATENSTRUKTUREN Pfade undKAPITEL Zyklen (II) A B D E C F Schleife Zyklus Beispiele für Wege: AA B B und F F,hierBeispiele fürWege. Pfade: E D B und C F B EED D B und C F FCwären Beispiele für E D B und C F sind Pfade. Für ungerichtete Graphen G gilt: Joost-Pieter Katoen Datenstrukturen und Algorithmen 11/36 - Ein Graph G heißt zusammenhängend, wenn jeder Knoten von jedem anderen Knoten aus erreichbar ist. - Eine Zusammenhangskomponente von G ist ein maximaler zusammenhängender Teilgraph von G. - In einer Ansammlung von Graphen heißt ein Graph maximal, wenn er von keinem anderen dieser Graphen ein echter Teilgraph ist. Für gerichtete Graphen G gilt: - G heißt stark zusammenhängend, wenn jeder Knoten von jedem anderen aus erreichbar ist. - G heißt schwach zusammenhängend, wenn der zugehörige ungerichtete Graph (wenn alle Kanten ungerichtet gemacht worden sind) zusammenhängend ist. - Eine starke Zusammenhangskomponente von G ist ein maximaler stark zusammenhängender Teilgraph von G. Elementare Graphenalgorithmen I Graphen Ein nicht verbundener Graph kann eindeutig in verschiedene ZusammenhangskompoStarke Zusammenhangskomponenten nenten aufgeteilt werden. Zusammenhangskomponenten Zusammenhangskomponenten Ein nicht-zusammenhängender Digraph, aufgeteilt in seine maximalen zusammenhängenden Teilgraphen. 13 Joost-Pieter Katoen Datenstrukturen und Algorithmen 16/36 KAPITEL 3. DATENSTRUKTUREN Darstellung als Adjazenzmatrix und Adjazenzliste Die Adjazenzmatrix-Darstellung eines Graphen ist durch eine nxn Matrix A gegeben, wobei A(i, j) = 1, wenn (vi , vj ) ∈ E, sonst 0. - Wenn G ungerichtet ist, ergibt sich eine symmetrische Adjazenzmatrix A (d.h. A = AT ). Dann muss nur die Hälfte der Datenmenge gespeichert werden. → Platzbedarf: Θ(n2 ) Bei der Darstellung als Array von Adjazenzlisten gibt es ein durch die Nummer des Knoten indiziertes Array, das jeweils verkettete Listen (Adjazenzlisten) enthält. - Der i-te Arrayeintrag enthält alle Kanten von G, die von vi „ausgehen“. - Ist G ungerichtet, dann werden Kanten doppelt gespeichert. - Kanten, die in G nicht vorkommen, benötigen keinen Speicherplatz. → Platzbedarf: Θ(n + m) Elementare Graphenalgorithmen I Graphen Darstellung eines gerichteten Graphen A D A B C D E F C B F E B E F B D F D E Adjazenzliste S W W W W W W U Joost-Pieter Katoen 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 1 0 0 0 0 0 1 0 0 1 Adjazenzmatrix T X X X X X X V Datenstrukturen und Algorithmen 20/36 Abbildung 3.1: Beispiel für die Darstellung als Adjazenzmatrix und Adjazenzliste 14 4 Flüsse 1 Allgemeine Flusseigenschaften 1.1 Flussnetzwerke Ein Flussnetzwerk ist ein gerichteter Graph, dessen Kanten die Zusatzinformation der Kapazität tragen (Kanten sind wie Wasserrohre).s, t ∈ V (Quelle s, Senke t) sind ausgezeichnete Knoten des Netzwerkes. – An der Quelle wird produziert – An der Senke wird verbraucht – Kapazität = maximale Durchsatzrate 1.2 Fluss in einem Flussnetzwerk Ein Fluss f hat folgende Eigenschaften: Beschränkung: Für v ∈ V gilt: f (u, v) ≤ c(u, v). Ein Fluss f ist also immer maximal so groß wie die Kapazität c. Asymmetrie: Für v ∈ V gilt: f (u, v) = −f (v, u) Wird die Flussrichtung umgekehrt, so ändert sich das Vorzeichen des Flusses. Flusserhaltung: Für u ∈ V − {s, t} gilt: Σv∈V f (u, v) = 0 Was in einen inneren Knoten, der weder Quelle noch Senke ist, hineinfließt kommt auch wieder heraus. – f(u,v) ist der Fluss vom Knoten u zum Knoten v. – Der Wert |f| eines Flusses f ist der Gesamtfluss aus der Quelle s: X |f | = f (s, V ) v∈V – Ein maximaler Fluss ist einen Fluss mit maximalem Wert für ein Flussnetzwerk. 15 Maximaler Fluss Flussnetzwerke Flüsse zwischen Knotenmengen KAPITEL 4. FLÜSSE Notationen Maximaler Fluss Flussnetzwerke ÿ f (x , Y Knotenmengen ) = f (x , y ) für Y 1.3 Flüsse zwischen Flüsse zwischen Knotenmengen y œY ÿ Notationen f (X , y ) = f (x , y ) ÿ x œX f (x , Y ) = ÿ ÿ f (x , y ) f (X , Y ) = f (X , y ) = y œY ÿ x œX y œY x œX für X ™ V für Y ™ V f (x , y ) für X , Y ™ V f (x , y ) ÿ ÿ Mengen Eigenschaften von Flüssen zwischen f (X , Y ) = nken geben. e“ in ein en geben. in ein 2) 1) 4 21/42 f (x , y ) für X ™ V für X , Y ™ V Falls f ein Fluss für Flussnetzwerk x œX yG œY= (V , E , c) ist, dann gilt: 1. f (X , X ) = 0 für X ™ V Eigenschaften Flüssen 2. f (X , Yvon ) = ≠f (Y , X )zwischen Mengen für X , Y ™ V 3. ff (X Y , Z )für = Flussnetzwerk f (X , Z ) + f (Y ,GZ )= (V für, EX,,c) Y ,ist, Z ™dann V : Xgilt: flY =? Falls einfiFluss 4. f (Z , fX(X fi ,YX))==f 0(Z , X ) + f (Z , Y ) fürfür X ,X Y ,™Z V™ V : X fl Y = ? 1. 2. f (X , Y ) = ≠f (Y , X ) Joost-Pieter Katoen 3. f (X fi Y , Z ) = f (X , Z ) + f (Y , Z ) Maximaler Fluss für X , Y ™ V Datenstrukturen und Algorithmen 2 Ford-Fulkerson-Methode 4. f (Z , X fi Y ) = f (Z , X ) + f (Z , Y ) 21/42 ™V 22/42 für X , Y , Z ™ V : X fl Y = ? für X , Y , Z ™ V : X fl Y = ? Flussnetzwerke Eingehender Fluss in der Senke Datenstrukturen und Algorithmen Prinzip Joost-Pieter Katoen 22/42 Wie groß ist der an der Senke eingehende Fluss? – Überführe Maximaler Flussden Graphen in ein Flussnetzwerk. Flussnetzwerke Aufgrund der Flusserhaltung in alle Zwischenknoten ist zu erwarten, dass er dem austretenden Fluss der Quelles entspricht: – Suche einen beliebigen Pfadan und t heraus. Eingehender Fluss inzwischen der Senke 4 groß ist der der Senke Fluss? f Kapazität (s,eingehende V ) = f (V(maximalen , t) –Wie Bestimme die an minimale Durchfluss) dieses Pfades. 4 –Aufgrund Bestimme Flusserhöhung, der Flussist entlang des Pfades Beweis: der die Flusserhaltung in alleindem Zwischenknoten zu erwarten, dass gleich der Kapazität der Kante mit der minimalen Kapazität gesetzt wird. er dem austretenden Fluss an der Quelle entspricht: f (s, V ) = f (V , V ) ≠ f (V ≠ {s}, V ) | Eigenschaft 3 – Bilde das Restnetzwerk, indem der Durchfluss als umgekehrte Kanten einge= ≠f (V ≠ {s}, V | Eigenschaft 1 (s,) V ) = f (V , t) zeichnet wird (eventuell fexistierende Gegenpfade werden verrechnet). 4 Beweis: = f (V , V ≠ {s}) | Eigenschaft 2 – Fahre solange fort, suchen, augmentierende (den Fluss = f neue (V , t)Pfade + f (V ,zu V≠ {s, t}) bis es keine | Eigenschaft 4 weiter vergrößernde) Pfade mehr gibt, d.h. die Senke noch von der Quelle aus | Flusserhaltung f (s, V ) = =ff(V (V, ,t). V ) ≠ f (V ≠ {s}, V ) | Eigenschaft 3 erreichbar ist. = ≠f (V ≠ {s}, V ) mmetrie. 23/42 | Eigenschaft 1 = f (V {s}) aller Flusserhöhungen. | Eigenschaft 2 → Der Maximale Fluss ist, V die≠Summe Joost-Pieter Katoen = f (V , t) + f (V , V ≠Datenstrukturen {s, t}) und Algorithmen | Eigenschaft 4 = f (V , t). 24/42 | Flusserhaltung metrie. 23/42 Joost-Pieter Katoen Datenstrukturen und Algorithmen 16 24/42 5 Algorithmenprinzipien 1 Divide & conquer - Teilen und Beherrschen Das Prinzip hinter Divide & conquer- Algorithmen ist, dass sie ein Problem in kleinere Teilprobleme teilen, die zwar dem ursprünglichen Problem ähneln, jedoch eine kleinere Größe besitzen. Die Teilprobleme werden dabei rekursiv gelöst. Anschließend werden die Lösungen der Teilprobleme dann kombiniert, um daraus die Lösung des Ursprungsproblems zu erhalten. Das Paradigma umfasst drei Schritte: - Teile das Problem in eine Anzahl von Teilproblemen auf. - Beherrsche die Teilprobleme durch rekursives Lösen. Hinreichend kleine Teilprobleme werden direkt gelöst. - Verbinde die Lösungen der Teilprobleme zur Lösung des Ausgangsproblems. 2 Greedy-Algorithmen Greedy(‘gierige’) Algorithmen sind dadurch gekennzeichnet, dass sie in jedem Schritt eine kuzfristig optimale Lösung wählen. Diese Lösungsfindung sollte eine möglichst geringe Komplexität haben. Nachdem eine Wahl für eine Lösung getroffen wurde ist diese nicht mehr rückgängig zu machen. Die Lösungsstrategie eines Greedy-Algorithmus’ ist optimal, wenn sie sich aus optimalen Teilproblemen zusammensetzt, die unabhängig von anderen Teillösungen sind. 3 Dynamische Programmierung Dynamische Programmierungs-Probleme sind Optimierungsprobleme, also Probleme, für die es verschiedene Lösungsmöglichkeiten mit einer Minimierung von Kosten oder einer Maximierung eines Wertes gibt. Damit Dynamische Programmierung möglich ist, muss die optimale Lösung aus sich überlappenden Teilproblemen bestehen, die sich rekursiv aufeinander beziehen. 17 KAPITEL 5. ALGORITHMENPRINZIPIEN Allgemeines Vorgehen 1. Stelle die Rekursionsgleichung (top-down) für den Wert der Lösung auf. – Dabei sind folgende Punkte zu klären: – Festlegung der Basisfälle – Unterscheidung zwischen Maximierungs- und Minimierungsproblem. – Festlegung der Abbruchbedingungen 2. Löse die Rekursionsgleichung bottom-up. 3. Bestimme aus dem Wert der Lösung die Argumente der Lösung. 4. Rekonstruiere die Lösung. – dies geschieht durch aufstellen einer Lösungstabelle, die entsprechend der Rekursionsgleichung ausgefüllt wird. 3.1 Rucksackproblem Gegeben sei ein Rucksack, mit maximaler Tragkraft M, sowie n Gegenstände, die sowohl ein Gewicht als auch einen Wert haben. Nehme möglichst viel Wert mit, ohne den Dynamische Programmierung Anwendungen Rucksack zu überladen. Das Rucksackproblem – Rekursionsgleichung (II) Rekursionsgleichung Sei also C [i, j] der maximale Wert des Rucksacks mit Tragkraft j, wenn Sei also C[i, j] der Wert des mit Tragkraft j, wenn nur die Gegenmanmaximale nur die Gegenstände { 0, .Rucksacks . . , i ≠ 1 } berücksichtigt. stände 0, ..., i − 1 berücksichtigt werden. Es ergibt sich folgende Rekursionsgleichung: C [i, j] = I Y _ ] max(C [i≠1, j], ci≠1 + C [i≠1, j≠wi≠1 ]) ≠Œ _ [ 0 für j < 0 für i = 0, j > 0 Dann ist cmax = C [n, M]. Diese Rekursionsgleichung lösen wir nun bottom-up, indem wir die Rucksäckewmit möglichenc Gewichten // Eingabe : Gewichte [ i ]allen , Werte [ i ] , T {r 0, a g. .k. r, M a f}t berechnen, M wenn wir jeweils einen weiteren Gegenstand hinzunehmen. int knapDP ( int w[ n ] , int c [ n ] , int n , int M) { I 1 3 5 7 9 11 int C[ n+1,M+ 1 ] ; // Array i n i t i a l i s i e r e n fo r ( int Joost-Pieter j = 0Katoen ; j <= M; j ++) Datenstrukturen und Algorithmen 22/31 C[ 0 , j ] = 0 ; // B a s i s f a l l fo r ( int i = 1 ; i <= n ; i ++){ fo r ( int j = 0 ; j <= M; j ++) i f (w [ i −1] <= j ) { C[ i , j ] = max(C[ i −1, j ] , c [ i −1] + C[ i −1, j −w[ i − 1 ] ] ) ; } else C[ i , j ] = C[ i −1, j ] ; // p a s s t n i c h t } 18 Das Rucksackproblem – Rekonstruktion Offen ist noch die Frage, Gegenstände (S ™ G) nun eigentlich KAPITEL 5. welche ALGORITHMENPRINZIPIEN mitgenommen werden müssen, um cmax zu erreichen. return C[ n ,M] ; 13 } Beispiel Beispiel w[] = { 2, 12, 1, 1, 4 }, c[] = { 2, 4, 2, 1, 10 }, M = 15 0 1 0 0 0 0 0 0 0 1 2 3 4 5 0 0 0 2 2 2 2 0 2 2 2 3 3 3 4 5 6 7 8 9 10 11 12 13 14 15 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 4 4 6 6 4 4 4 4 4 4 4 4 4 4 6 6 8 4 5 5 5 5 5 5 5 5 5 6 7 8 4 10 12 13 14 15 15 15 15 15 15 15 15 Joost-Pieter Katoen Datenstrukturen und Algorithmen 25/31 3.2 Longest Common Subsequence (LCS) Sei A = (a1 , ..., am ) eine Sequenz. Die Sequenz Aik = (ai1 , ..., aik ) ist eine Teilsequenz von A wobei i1 < i2 < ... < ik und ij ∈ 1, ..., m. Dynamische Programmierung Anwendungen Beispiel: bca ist eine gemeinsame Teilsequenz von A = abcbdab und B = bdcaba. Longest Common Subsequence – Rekursionsgleichung Rekursiongleichung Hier lässt sich die Rekursiongleichung für den Wert, also die Länge der LCS aufstellen: Wir können wieder die Rekursiongleichung für den Wert, also die Länge L[i, j] = |LCS(A i , Bj )| der LCS aufstellen: L[i, j] = |LCS(Ai , Bj )| L[i, j] = 2 4 6 8 10 12 Y 0 _ _ ] _ _ [ für i = 0 oder j = 0 L[i ≠ 1, j ≠ 1] + 1 falls ai = bj , i, j > 0 max(L[i, j ≠ 1], L[i ≠ 1, j]) falls ai ”= bj , i, j > 0 I Das lässt sich direkt als Algorithmus umsetzen (Hausaufgabe). I Dessen Laufzeit ist O(|A|·|B|), ebenso seine Platzkomplexität. int l c s ( int Is e q 1 [ ] , int l 1 , int s e q 2 [ ] , int l 2 ) { Ähnlich dem Rucksackproblem lässt sich dann die LCS rekonstruieren. int L [ l 1 +1, l 2 + 1 ] ; fo r ( int i = 0 ; i i= l 1 ; i ++) L[ i , 0 ] = 0; Joost-Pieter Katoen Datenstrukturen und Algorithmen 30/31 fo r ( int j = 0 ; j i= l 2 ; j ++) L[0 , j ] = 0; fo r ( int i = 1 ; i i= l 1 ; i ++) { fo r ( int j = 1 ; j i= l 2 ; j ++) { i f ( s e q 1 [ i ] == s e q 2 [ j ] ) { L[ i , j ] = L[ i − 1 , j − 1] + 1; } e l s e i f (L [ i − 1 , j ] > L [ i , j − 1 ] ) { 19 KAPITEL 5. ALGORITHMENPRINZIPIEN L[ i , j ] = L[ i − 1 , j ] ; } else { L[ i , j ] = L[ i , j − 1]; } 14 16 } return L [ l 1 , l 2 ] ; 18 } 20 6 Sortieralgorithmen 1 Sortieren Einfache Sortieralgorithmen Sortieren durch Einfügen 1.1 Sortieren Insertionsort - Sortieren durch einfügen durch Einfügen – Insertionsort Prinzip: 0 12 17 17 19 8 25 3 6 69 26 4 2 13 34 41 bereits sortiert noch unsortiert als Nächstes einzusortieren - Beginne beim zweiten Element. I Durchlaufen des (unsortierten) Arrays von links nach rechts. - Der unsortierte Bereich des Arrays wird von links nach rechts durchlaufen. - Gehe zun ersten bisher noch nicht berücksichtigten Element. - Suche die richtige Position für das Element im bereits sortierten Arrayabschnitt. - Füge das Element an die richtige Stelle ein. - Verschiebe alle bereits sortierten Elemente Rechts vom eingefügten Element um 1 nach rechts. ⇒Joost-Pieter Wiederhole erreicht ist. Katoen solange, bis das Ende des Arays Datenstrukturen und Algorithmen 1 3 5 7 9 11 void i n s e r t i o n S o r t ( n ) { fo r ( i = 1 ; i < n ; i ++){ i f (E [ i ] < E [ i −1]){ int temp = E [ i ] ; f o r ( j = i ; j >0 && E [ j −1] > temp ; j −−){ E[ j ] = E[ j −1]; } E [ j ] = temp ; } } } 21 17/32 KAPITEL 6. SORTIERALGORITHMEN 1.2 Selectionsort Prinzip: 1. Setze Pointer i auf das erste Element. 2. Suche kleinstes Element rechts von i und speichere die Position dieses Elementes als m. 3. Vertausche i. Element mit dem Wert des Elementes an Position m. Setze i um eins hoch. ⇒ Wiederhole 2./3. bis Ende des Arrays erreicht. 1 3 5 7 9 11 13 void s e l e c t i o n S o r t ( Array E) { int i , j ,m; fo r ( i = 0 ; i < E . l e n g t h ; i ++) { m= i; fo r ( j = i + 1 ; j < E . l e n g t h ; j ++) { i f (E [ j ] <= E [m] ) { m= j; } } int v = E [ i ] ; E [ i ] = E [m] ; E [m] = v ; } } 2 Höhere Sortieralgorithmen 2.1 Heapsort Prinzip: 1. Heapsort: - buildHeap: Heap aus Array aufbauen. - Beginne beim letzten Element und gehe nach vorne durch, bis der Arrayanfang erreicht ist: - Tausche das erste Arrayelement mit dem aktuell betrachteten. - Heapify: Stelle die Heapeigenschaft wieder her. 2. Heapify (Heapeigenschaft wiederherstellen): - Beginne beim letzten Element und gehe rückwärts bis zum ersten Element: 22 KAPITEL 6. SORTIERALGORITHMEN - Finde das Maximum der Werte A[i] und seiner Kinder - Is A[i] bereits das größte Element, dann ist der gesamte Teilbaum auch ein Heap. - Sonst: Tausche A[i] mit dem größten Element und führe Heapify erneut in diesem Unterbaum aus. 2 4 void buildHeap ( int E [ ] ) { fo r ( int i = E . l e n g t h / 2 − 1 ; i >= 0 ; i −−) { h e a p i f y (E , E . l e n g t h , i ) ; } } 6 8 10 12 14 16 18 20 22 24 26 28 void h ea p S o r t ( int [ ] E) { buildHeap (E ) ; fo r ( int i = E . l e n g t h − 1 ; i > 0 ; i −−) { swap (E [ 0 ] , E [ i ] ) ; h e a p i f y (E , i , 0 ) ; } } void h e a p i f y ( int [ ] E , int n , int pos ) { int next = 2 ∗ pos + 1 ; while ( next < n ) { ( next + 1 < n && E [ next + 1 ] > E [ next ] ) { next = next + 1 ; } i f (E [ pos ] > E [ next ] ) { break ; } swap (E [ pos ] , E [ next ] ) ; pos = next ; next = 2 ∗ pos + 1 ; } } 23 KAPITEL 6. SORTIERALGORITHMEN Beispiel für Heapsort mit Eingabearray E = [12, 6, 8, 3, 4, 0, 2] 2.2 Countingsort Prinzip: - Zunächst ein Histogramm- Hilfsarray erstellen, in dem die Häufigkeit jeder Zahl gespeichert wird. - Positionsarray erstellen, in dem von links nach rechts die Werte des HistogrammArrays aufaddiert werden. Beispiel: Histogramm: Positionen: 2 2 0 2+0 0 2+0 1 2+1 2 3+2 1 5+1 - Ausgabearray von hinten nach vorne erstellen, indem die neue Position der Zahl X im Eingabearray der Wert an Position (X − 1) des Positionsarrays ist. - Der Wert des Positionsarrays wird anschließend um 1 verringert. 24 KAPITEL 6. SORTIERALGORITHMEN Beispiel: Eingabe: 6 8 0 0 1 Histogramm: 2 0 Positionen 2 2 Ausgabearray 0 1 2 3 4 5 4 0 4 0 4 4 0 4 4 5 0 3 4 4 5 0 0 3 4 4 5 0 0 3 4 4 5 0 0 3 4 4 5 1 3 5 7 9 11 13 15 17 3 5 4 2 0 2 3 1 3 6 7 6 8 8 0 4 2 5 0 2 1 1 1 1 0 0 0 4 5 1 6 1 2 2 2 2 2 2 2 2 6 1 7 7 8 0 1 7 8 Positionsarray 2 3 4 5 6 2 3 4 6 7 2 3 4 6 7 2 3 3 6 7 2 3 3 5 7 2 2 3 5 7 2 2 3 5 7 2 2 3 5 7 2 2 3 5 7 7 7 7 7 7 7 7 7 6 8 8 8 8 8 8 8 7 7 int [ n ] c o u n t i n g S o r t ( int [ ] E , int n , int k ) { int [ ] h i s t o g r a m = new int [ k ] ; // D i r e k t e A d r e s s i e r u n g s t a b e l l e fo r ( int i = 0 ; i < 0 ; i ++){ histogramm [ E [ i ] ] + + ; // Zä h l e Hä u f i g k e i t } fo r ( int i = 1 ; i < k ; i ++){ // Berechne P o s i t i o n histogramm [ i ] = h i s t o g r a m [ i ] + h i s t o g r a m [ i − 1 ] ; } // Berechne P o s i t i o n int e r g e b n i s [ n ] ; fo r ( int i = n − 1 ; i >= 0 ; i −−){ // l ä u f t nur s t a b i l , wenn r ückwä r t s g e z ä h l t wird . h i s t o g r a m [ E [ i ]] − −; r e s u l t [ histogram [E[ i ] ] ] = E[ i ] ; } return r e s u l t ; } 2.3 Mergesort Prinzip: - Teile das Zahlenarray in zwei (möglichst) gleichgroße Hälften auf. - Beherrsche: Sortiere die Teile durch rekursives Mergesort-aufrufen. - Verbinde je zwei bereits sortierte Teilsequenzen zu einen einzigen sortierten Array. 25 KAPITEL 6. SORTIERALGORITHMEN 1 3 5 7 9 // A u f r u f : mergeSort (E , 0 , E . l e n g t h −1); void mergeSort ( Array E , int l i n k s , int r e c h t s ) { if ( l e f t < right ) { int mid = ( l e f t + r i g h t ) / 2 ; // f i n d e Mitte mergeSort (E , l e f t , mid ) ; // s o r t i e r e l i n k e H a e l f t e mergeSort (E , mid + 1 , r i g h t ) ; // s o r t i e r e r e c h t e H a e l f t e // Verschmelzen d e r s o r t i e r t e n H a e l f t e n merge (E , l e f t , mid , r i g h t ) ; } } 11 13 15 17 19 21 23 25 27 void merge ( int E [ ] , int l e f t , int mid , int r i g h t ) { int a = l e f t , b = mid + 1 ; int Eold [ ] = E ; fo r ( int l e f t = 0 ; l e f t <= r i g h t ; l e f t ++) { i f ( a > mid ) { // Wir w i s s e n ( Widerspruch ) : b <= r i g h t E [ l e f t ] = Eold [ b ] ; b++; } e l s e i f ( b > r i g h t | | Eold [ a ] <= Eold [ b ] ) { E [ l e f t ] = Eold [ a ] ; a++ } else { E [ l e f t ] = Eold [ b ] ; b++; } } } 2.4 Quicksort Prinzip Die Elemente werden zunächst auf die richtige Hälfte des Arrays gebracht, und dann rekursiv sortiert. 1. Teile: Ein Pivotelement aus dem Array auswählen, Array in zwei Hälften aufteilen: - Kleiner als das Pivotelement - Mindestens so groß wie das Pivotelement 2. Beherrsche: Die Teile rekursiv sortieren, das Pivotelement zwischen die sortierten Teile setzen. Prinzip der Partitionierung: - Es werden drei Bereiche eingesetzt: “< Pivot”, “> Pivot” und “ungeprüft”. 26 Quicksort Quicksort KAPITEL 6. SORTIERALGORITHMEN Quicksort – Strategie 41 26 17 25 19 17 8 3 6 69 12 4 2 13 34 0 Pivot Partitionierung 8 3 6 12 4 2 13 0 17 41 26 17 69 25 19 34 < Pivot > Pivot - Solange das zusätzliche Element “< Pivot” ist, schicke die linke Grenze nach rechts. - Solange das zusätzliche Element “>= Pivot” ist, schiebe die rechte Grenze nach links. - Tausche das links gefundene mit dem rechts gefundenen. ⇒ Fahre fort, bis sich die Grenzen treffen. Joost-Pieter Katoen 1 3 5 7 Datenstrukturen und Algorithmen 6/20 void q u i c k S o r t ( int E [ ] , int l e f t , int r i g h t ) { if ( l e f t < right ) { int i = p a r t i t i o n (E , l e f t , r i g h t ) ; // i i s t P o s i t i o n d e s P i v o t e l e m e n t e s q u i c k S o r t (E , l e f t , i − 1 ) ; // s o r t i e r e den l i n k e n T e i l q u i c k S o r t (E , i + 1 , r i g h t ) ; // s o r t i e r e den r e c h t e n T e i l } } 9 11 13 15 17 19 21 23 int p a r t i t i o n ( int E [ ] , int l e f t , int r i g h t ) { // Wä h l e e i n f a c h e s P i v o t e l e m e n t int ppos = r i g h t , p i v o t = E [ ppos ] ; while ( true ) { // B i l i n e a r e Suche while ( l e f t < r i g h t && E [ l e f t ] < p i v o t ) l e f t ++; while ( l e f t < r i g h t && E [ r i g h t ] >= p i v o t ) r i g h t −−; i f ( l e f t >= r i g h t ) { break ; } swap (E [ l e f t ] , E [ r i g h t ] ) ; } swap (E [ l e f t ] , E [ ppos ] ) ; return l e f t ; // g i b neue P i v o t p o s i t i o n a l s S p l i t p u n k t z u r ück } 2.5 Dutch National Flag- Problem Prinzip: Jedes Feld eines Arrays kann drei mögliche Werte haben: Rot, Weiß oder Blau. 27 KAPITEL 6. SORTIERALGORITHMEN - Teile das Array in vier Regionen auf: 1. 0 < i ≤ r Sortieren Sortieren - Einführung 2. u < iFlag < b Problem (IV) Dutch National 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 3. b ≤ i ≤ n void DutchNationalFlag(Color E[], int n) { int r = 0, b = n + 1; // rote und blaue Regionen sind leer gibt Zeiger: int -u Es = 1; // 3weiße Region ist leer, die unbekannte == E while (u < b) { - r: if (E[u] == Rote rot) {Region swap(E[r 1], E[u]); - b:+ Blaue Region r = r + 1; // vergrößere die rote Region u = u -+ u: 1; Unbekannte // verkleinere die unbekannte Region Region } if (E[u] == weiss) { r u b u = u + 1; } if (E[u] == blau) { swap(E[b - 1], E[u]); b = b - 1; //Zeigerstartposition vergrößere die blaue Region beim Dutch National Flag- Problem } } } - Steht u auf einem roten Feld, wird E[u] mit E[r + 1] vertauscht und der rote Bereich vergrößert und der unbekannte Bereich um 1 verringert (r++, u++) Joost-Pieter Katoen Datenstrukturen und Algorithmen 13/32 - Steht u auf einem weißen Feld, wird der unbekannte Bereich um 1 verkleinert (u++) - Steht u auf einem blauen Feld, wird E[u] mit E[b−1] vertauscht. Der blaue Bereich erhöht sich um 1. (b–, u bleibt gleich!) ⇒ Der Algorithmus terminiert, wenn u = b. In jedem Schritt wird entweder u erhöht, oder b verringert. 2 4 6 8 10 12 14 16 18 void Dut c h N a t i o n a l F l a g ( C o l o r E [ ] , int n ) { int r = 0 , b = n + 1 ; // r o t e und b l a u e Regionen s i n d l e e r int u = 1 ; // wei ß e Region i s t l e e r , d i e unbekannte == E while ( u < b ) { i f (E [ u ] == r o t ) { swap (E [ r + 1 ] , E [ u ] ) ; r = r +1; // v e r g r ö ß e r e d i e r o t e Region u = u + 1 ; // v e r k l e i n e r e d i e unbekannte Region } i f (E [ u ] == w e i s s ) { u = u + 1; } i f (E [ u ] == b l a u ) { swap (E [ b − 1 ] , E [ u ] ) ; b = b−1; // v e r g r ö ß e r e d i e b l a u e Region } } } 28 KAPITEL 6. SORTIERALGORITHMEN 3 Gesamtübersicht über Sortieralgorithmen Laufzeitkomplexität Algorithmus Worst case Average case Speicher Stabilität Insertionsort Θ(n2 ) Θ(n2 ) in-place stabil 2 Selectionsort Θ(n ) Θ(n2 ) in-place nicht stabil (*) Quicksort Θ(n2 ) Θ(n log n) Θ(log n) nicht stabil(*) Mergesort Θ(n log n) Θ(n log n) Θ(n) stabil Heapsort Θ(n log n) Θ(n log n) in-place nicht stabil 1 Countingsort Θ(n + k) Θ(n + k) O(n + k) stabil Dutch National Flag Θ(n) Θ(n) in place nicht stabil (*): Vorlesungsversion ist nicht stabil, es existiert jedoch auch eine stabile Version. 29 7 Graphenalgorithmen 1 Graphensuche 1.1 Tiefensuche (Tiefensuche: Depth-First Search, DFS) Prinzip: – Alle Knoten als ’nicht gefunden’ markieren. – Aktuellen Knoten als ’gefunden’ markieren. – Jede Kante mit ’gefundenem’ Nachfolger : – Erforsche Kante, besuche Nachfolger und forsche von dort aus weiter bis es nicht mehr weiter geht. – Für jede Kante mit gefundenem Nachfolger: – Kante überprüfen ohne den Knoten selbst zu besuchen. – Knoten als ’abgeschlossen’ markieren. ⇒ Man erhält die Menge der vom Startknoten aus erreichbaren Knoten. 30 Elementare Graphenalgorithmen I Graphendurchlauf Tiefensuche – Beispiel (I) KAPITEL 7. GRAPHENALGORITHMEN A D A B F B G E C F Beginn der Tiefensuche A B E F D Tiefensuche – Beispiel (II) B G G F A B E C D ist eine Sackgasse Backtracke und erforsche den nächsten Knoten A F F F C B C A G E E Elementare Graphenalgorithmen I F B B D C E G D G B F F E G G E E Erforsche den nächsten Knoten D GGraphendurchlauf F Beide nächsten Knoten wurden bereits gefunden C C Datenstrukturen A undBAlgorithmen Tiefensuche – Implementierung F C B Erforsche den nächsten Knoten Backtracke und Aerforsche den nächsten KnotenA D A D A C wurde bereits gefunden Backtracke und bereits erforsche den nächsten Knoten C wurde gefunden Joost-Pieter Katoen E C B ist eine Sackgasse Backtracke und erforsche den nächsten Knoten D B D G D A 29/36 GraphendurchlaufB G Tiefensuche – Beispiel (III) F E C Nächster Zustand wurde bereits gefunden Datenstrukturen und Algorithmen Backtracke und erforsche den nächsten Knoten D Elementare Graphenalgorithmen I D B Nächster Zustand wurde bereits gefunden Joost-Pieter Katoen Backtracke und erforsche den nächsten Knoten A E C Graphendurchlauf A E C G Sackgasse! Backtracke und erforsche den nächsten Knoten Erforsche einen Knoten F D G Elementare Graphenalgorithmen I A E C A B C G Erforsche einen Knoten D F D C G D B 30/36 G E Fertig! F C E Beispiel fürint eine Tiefensuche BeidedfsRec(List nächsten Knoten wurden bereits gefunden void adjList[n], n, int start, Fertig! int &color[n]) { 2 color[start] = GRAY; 3 foreach (next in adjList[start]) 31 { 4 if (color[next] == WHITE) { 5 dfsSRec(adjList, n, next, color); Joost-Pieter Katoen Datenstrukturen und Algorithmen 6 } 7 } 8 color[start] = BLACK; 9 } 1 31/36 KAPITEL 7. GRAPHENALGORITHMEN 2 4 6 void d f s S e a r c h ( L i s t a d j L i s t [ n ] , int n , int s t a r t ) { int c o l o r [ n ] ; fo r ( int i = 0 ; i < n ; i ++){ // F arbarray i n i t i a l i s i e r e n c o l o r [ i ] = WHITE; } dfsRec ( adjList , n , s t a r t , c o l o r ) ; } 8 10 12 14 16 void d f s R e c ( L i s t a d j L i s t [ n ] , int s t a r t , int &c o l o r [ n ] ) { c o l o r [ s t a r t ] = GRAY; // A k t u e l l b e t r a c h t e t e n Knoten grau f ä rben f o r e a c h ( next i n a d j l i s t [ s t a r t ] ) { i f ( c o l o r [ next ] == WHITE) d f s R e c ( a d j L i s t , n , next , c o l o r ) ; } c o l o r [ s t a r t ] = BLACK; // Gefundenen S t a r t k n o t e n schwarz f ä rben } 1.2 Breitensuche Prinzip: – Alle Knoten als ’nicht gefunden’ markieren – Aktuell betrachteten Knoten als ’gefunden’ markieren – Jede Kante mit ’nicht gefundenem’ Nachfolger: – Gleichzeitig von allen diesen Knoten aus weiter suchen → Kein Backtracking ⇒ Man erhält die Menge aller Knoten, die vom Stratknoten aus erreichbar ist. 32 Elementare Graphenalgorithmen I Graphendurchlauf 7. GRAPHENALGORITHMEN Breitensuche KAPITEL – Beispiel A D B F C A A E F C C G E Erforsche alle folgenden nicht-gefundenen Knoten A D B F B G Beginn der Breitensuche D D B G F E Erforsche alle folgenden nicht-gefundenen Knoten C G E Fertig! Beispiel für eine Breitensuche Joost-Pieter Katoen Datenstrukturen und Algorithmen 25/36 void b f s S e a r c h ( l i s t a d j l i s t ( n ) , int n , int s t a r t ) { 2 int c o l o r [ ] ; Queue w a i t ; // ialisieren Elementare Graphenalgorithmen I Warteschlange i n i tGraphendurchlauf 4 // A l l e Knoten a l s ’ n i c h t gefunden ’ m a r k i e r e n fo r ( int i = 0 ; i < n ; i ++) 6 c o l o r [ i ] = WHITE; c o l o r [ s t a r t ] = GRAY; // S t a r t k n o t e n a l s ’ gefunden ’ m a r k i e r e n I wKnoten 8 a i t . enqueue ( s t ain r t der ) // SReihenfolge t a r t k n o t e n amit u f zunehmenden Warteschlange Abstand s e t z e n vom werden While ( ! Wait . isempty ()){ Startknoten aus besucht. 10 int v = w a i t . dequeue ( ) ) ; // Knoten a l s b e a r b e i t e t von queue nehmen I Nachdem alle Knoten mit Abstand d verarbeitet wurden, werden die f o r e a c h (w i n a d j l i s t [ v ] ) { // Die N a c h f o l g e r k n o t e n von v b e t r a c h t e n mit 12 i f dc o+l o1r angegangen. [ w] == WHITE { I Die Suche c o l o r [terminiert, w] = GRAY;wenn in Abstand d keine Knoten auftreten. 14 w a i t . enqueue (w ) ; // N a c h f o l g e r k n o t e n a l s nä c h s t e S t a r t k n o t e n I Die Tiefe des Knotens v im Breitensuchbaum ist seine kürzeste } Kantendistanz zum Startknoten. 16 } } I Die zu verarbeitenden Knoten werden als FIFO-Queue (first-in 18 c o l o r [ v ] = BLACK; // Gefundenen Knoten a l s ’ a b g e s c h l o s s e n ’ m a r k i e r e n first-out) organisiert. } Eigenschaften der Breitensuche Es gibt eine einzige „Verarbeitungsmöglichkeit“ für v , nämlich, wenn es aus der Algorithmus Queue entnommen wird. 2 Sharir’s I Theorem (Komplexität der Zusammenhangskomponenten. Breitensuche) Sharir’s Algorithmus findet starke Die Zeitkomplexität ist O(|V | + |E |), der Platzbedarf ⇥(|V |). Joost-Pieter Katoen 33 Datenstrukturen und Algorithmen 27/36 7 } void dfsSearch(List adjL[n], int n, int start, int &color[n]) { KAPITEL 7. GRAPHENALGORITHMEN 10 color[start] = GRAY; 11 foreach (next in adjL[start]) { Prinzipif (color[next] == WHITE) { 12 13 dfsSearch(adjL, n, next, color); – Tiefensuche auf Graph 14 }– Alle Knoten, die ’abgeschlossen’ sind, also gefunden wurden, auf einem Stack 15 } speichern. 16 color[start] = BLACK; // Schliesse ab 17 }– Graph transponieren und auf diesem transponierten Graphen eine weitere Tiefen9 suche durchführen. Dabei in der Reihenfolge die Knoten besuchen, in der sie auf dem Stapel stehen. Joost-Pieter Katoen – Leiter Algorithmen speichern. Leiter sind Knoten, dieDatenstrukturen als erste beiundder 2. Tiefensuche ’entdeckt’10/33 wurden. Elementare Graphenalgorithmen II Starke Zusammenhangskomponenten ⇒ Die starken Zusammenhangskomponenten sind die im stapel zwischen zwei Leitern. Sharir’s Algorithmus – Beispiel Beispiel: A D B F C A B G E Ursprünglicher Digraph E G A F B D C Stack am Ende der Phase 1 D F C G E Transponierter Graph A D B F C G E In Phase 2 gefundene starke Komponenten Zeitkomplexität Joost-Pieter Katoen Algorithmen Sharir’s Algorithmus hat eine ZeitkomplexitätDatenstrukturen von Θ(|V | und + |E|). Seine Speicherkomplexität beträgt: Θ(|V |). 3 Prims Algorithmus Der Algorithmus findet minimale Spannbäume. 34 12/33 KAPITEL 7. GRAPHENALGORITHMEN Prinzip – Bei beliebigen Knoten beginnen, Wert dieses Knotens auf 0, den der anderen auf ∞ setzen. – Günstigste Kante bestimmen (→ Greedy- Algorithmus), Werte der angrenzenden Knoten updaten, wenn Kantengewicht kleiner als der Wert ist. – Bei günstigem Knoten fortfahren, bis keine weiteren Randknoten mehr verfügbar sind. – Bei gleichem Gewicht wird in alphabetischer Reihenfolge vorgegangen. 4 Single Source Shortest Path (SSSP) 4.1 Dijkstra-Algorithmus Dijkstra ermittelt die kürzesten Wege vom Startknoten zu allen anderen Knoten des Graphen. Hier sind (im Gegensatz zu Bellman Ford) ausschließlich nicht negative Kantengewichte zugelassen. – Initialisiere alle Knoten mit ∞ – Aktualisiere ausgehend vom aktuell betrachteten Knoten alle Knoten, die mit diesem verknüpft sind, wenn die Summe aus aktuellem Knoten und der Kante geringer Lehrstuhl für Informatik 2 Datenstrukturen und Algorithmen SoSe 2012 ist als der Wert des Zielknotens. Betrachte den aktuellen Knoten zukünftig nicht Modellierung und Verifikation von Software Musterlösung - Übung 11 mehr. 2 Die einzige starke Zusammenhangskomponente ist der gesamte (vollständige) Graph. Ihr Gesamtgewicht ist – Fahre mit dem Knoten mitdesminimalem Wert fort. 3. Somit würde das Verfahren Studenten FALSE ausgeben. Es gibt jedoch einen Zyklus mit negativem Gewicht von A nach B und zurück. → Greedy-Algorithmus ⇒ Führe diese Schritte in einer (großen) Tabelle aus (siehe Beispiel). Beispiel 34 A 1 D 32 13 7 Schritt Knoten D[A] D[B] D[C] D[D] D[E] D[F ] D[G] D[H] D[I] 0 A 0 34 1 1 1 1 1 1 1 1 D X 33 1 1 1 1 6 8 1 2 G X 33 1 X 1 1 6 8 1 6 10 H 3 H X 33 1 X 10 1 X 8 18 35 14 11 2 3 C 4 E 12 5 G 9 B 4 E X 33 1 X 10 21 X X 16 F 8 I 5 I X 33 1 X X 21 X X 16 6 F X 25 35 X X 21 X X X 7 B X 25 34 X X X X X X 8 C X X 34 X X X X X X 3 G 10 H I KAPITEL 7. GRAPHENALGORITHMEN Schritt Knoten D[A] D[B] D[C] D[D] D[E] D[F ] D[G] D[H] D[I] 0 A 0 34 1 1 1 1 1 1 1 1 D X 33 1 1 1 1 6 8 1 2 G X 33 1 X 1 1 6 8 1 3 H X 33 1 X 10 1 X 8 18 4 E X 33 1 X 10 21 X X 16 5 I X 33 1 X X 21 X X 16 6 F X 25 35 X X 21 X X X 7 B X 25 34 X X X X X X 8 C X X 34 X X X X X X 4.2D[H] Bellman-Ford-Algorithmus D[I] Bellman Ford bestimmt den Kürzesten Pfad von einem Startknoten zu allen anderen b) Beweisen Sie dem die Korrektheit des Dijkstra d.h. zeigen Sie, dass dieser Algorithmus für einen Knoten nach SSSP (single sourceAlgorithmus, shortest path)Prinzip. Graphen G den kürzesten Abstand vom Startknoten s zu jedem anderen von s erreichbaren Knoten in G Wichtigster Unterschied zu Dijkstra ist, dass er auch mit negativen Kantengewichten berechnet. Sie dürfen dazu das Theorem aus Vorlesung 17, Folie 19 nutzen. umgehen kann. c) Beweisen oder widerlegen Sie die folgenden Aussagen für einen gewichteten, zusammenhängenden, ungerichteten Graphen G: Prinzip i) Der vom Dijkstra Algorithmus berechnete SSSP-Baum ist ein Spannbaum. 1. Initialisiere alle Knoten mit ∞ ii) Der vom Dijkstra Algorithmus berechnete SSSP-Baum ist ein minimaler Spannbaum. 2. Initialisiere den Startknoten mit 0 3. Aktualisiere die Kosten aller direkt durch Kanten erreichbaren Knoten, wenn durch die Summe des Ursrungsknotens und 3der Kosten der Kante ein geringerer Wert erzielt wird, als der Zielknoten bislang hatte. 4. Wiederhole 3), bis sich die Kosten nicht mehr ändern. → Breche ab, wenn ein negativer Zyklus vorliegt. Dies ist der Fall, wenn nach (Knotenanzahl - 1)- Wiederholungen noch Verbesserungen möglich sind. ⇒ Führe diese Schritte in einer Tabelle aus (siehe Beispiel). 36 Aufgabe 1 (Bellman-Ford Algorithmus): (5 + 6 + 3 = 14 Punkte) KAPITEL 7. GRAPHENALGORITHMEN a) Bestimmen Sie für den Startknoten A die Längen der kürzesten Wege zu jedem Knoten im unten gegebenen Graphen G1 mit Hilfe des Bellman-Ford Algorithmus. Dabei sei die Reihenfolge der Knoten gegeben durch Beispiel die alphabetische Sortierung (A, B, C, . . . ). Geben Sie für jeden Knoten K die Kosten an, die dem Knoten während der Berechnun zugewiesen werden. Z.B. im Format K : 1, 10, 7, 6, 3. Geben Sie außerdem an, ob der Algorithmus einen Zyklus Gewicht erkennt. 7 A 5 2 D B 3 8 -4 1 E 6 1/A 1/B C A: B: C: D: E: F: 1 F 1 1 1 1 1 1 1/E 1/F 2/B 2/F 0 7 6 15 4 3 2 5 3 2 A: B: C: D: E: F: 1, 1, 1, 1, 1, 1, 0 7, 6 15, 4, 3 2 5 3, 2 Der Graph enthält keinen Zyklus mit negativem Gewicht. b) Passen Sie die Implementierung von haben (Vorlesung 17, Folie 13), so an, dass der kürzeste Pfad zwischen zwei Knoten i und j ausgegeben (nicht zurückgegeben!) wird. Sie dürfen davon ausgehen, dass der übergebene Graph keine negativen Zykel besitzt. g Ihre Funktion soll die folgende Signatur haben: 5 Topologische Sortierung Topologische Sortierung bezeichnetadjLst[n], eine Reihenfolge Dingen, bei der vorvoid bellFord(List int n, intvon i, int j) gegebene Abhängigkeiten erfüllt sind. Anstehende Tätigkeiten einer Person Zuretwa Ausgabe können Sie annehmen, dass eine Funktion ausgabe mitBedingungen folgender Signatur unterliegen einer Halbordnung: es existieren wieexistiert: „Tätigkeit A muss vor Tätigkeit B erledigt werden“. Eine Reihenfolge, welche alle void ausgabe(String text) Bedingungen erfüllt, nennt man topologische Sortierung der Menge anstehenderkönnen Tätigkeiten. Einedass Topologische Sortierung nur konvertiert möglich, werden wenn können. es Außerdem Sie annehmen, int-Werte automatisch nachist String keinen Zyklus gibt, d.h. wenn keine gegenseitigen Abhängigkeiten vorliegen. c) Einem Studenten gefällt die Laufzeit des Bellman-Ford Algorithmus nicht und er schlägt das folgende alternative Verfahren vor, um Zyklen mit negativem Gesamtgewicht zu erkennen: WirPrinzip: benutzen Sharirs Algorithmus, um alle starken Zusammenhangskomponenten zu finden. Dies kostet O(|V | + |E|). Da |E| 2 O(|V |2 ) sind die Kosten damit in O(|V | + |V |2 ) = O(|V |2 ). Für jede starke Zusammenhangskomponente berechnen wir dann die Summe der Gewichte ihrer Kanten. Dies kostet insgesamt – Stelle die Abhängigkeiten als Graph dar. O(|E|) und ist also wiederum in O(|V |2 ). Ist eine dieser Summen negativ, geben wir TRUE aus, sonst FALSE. – Die gerichteten Kanten zeigen jeweils zum Knoten, von dem der Ausgangsknoten ist Wo liegt der Fehler, den der Studentabhängig begangen hat? – Sortiere die Knoten nach ihrer Abhängigkeiten so dass rechts diejenigen Knoten stehen, von denen nichts abhängt und links diejenigen Knoten stehen, die keine eigenen Abhängigkeiten haben – Versehe alle Knoten mit den jeweiligen Kosten. 1 ⇒ Der Kritische Pfad ist der Pfad mit den maximalen Kosten (betrachtet von links nach rechts). Der kritische Pfad ist eine Folge von Aufgaben v0 . . . vk , sodass - v0 keine Abhängigkeiten hat - vi abhängig von vi−1 ist, wobei est(vi ) = ef t(vi−1 ) (Keine Verzögerung!) est: earliest starting-time 37 KAPITEL 7. GRAPHENALGORITHMEN eft: earliest finish-time - ef t(vk ) das Maximum über alle Aufgaben ergibt. Beispiel Arbeitsschritt Vorlesung Folien Lehrstoff Globalübung Musterlösung Aufgaben Blatt Tutorenbesprechung Abkürzung V F L G M A B T Dauer 1 4 1 1 2 3 1 1 Abhängig von F L A, M A L A A, M L F V A M G B 1 4 1 3 2 1 1 T Darstellung der Abhängigkeiten als gerichteter Graph und als Topologische Sortierung mit kritischem Pfad. – Vollständige Tiefensuche, bei der zusätzlich ein Array ’kritische Länge’ und ’earliest finish-time’ übergeben wird. – Nach der Abfrage der Knotenfarbe wird jedes mal folgende Abfrage durchgeführt: 1 3 i f ( e f t [ next ] >= e s t ) { e s t = e f t [ next ] c r i t D e p [ s t a r t ] = next ; 38 yd All-Pairs Shortest Path Der Algorithmus von Floyd All-Pairs Shortest Paths KAPITEL 7. GRAPHENALGORITHMEN Wir betrachten gewichtete Digraphen G = (V , E , W ). I I Die Funktion W ordnet Kanten ein Gewicht zu. }Negative Gewichte sind zugelassen, aber keine Zyklen mit negativem Gewicht. – Bevor Ider Knoten schwarz gefärbt wird, wird Nicht vorhandene Kanten haben Gewicht W (·, ·) = Œ. e f t [ s t a r t ] = est + duration [ s t a r t ] Problem (All-Pairs Shortest Path) Berechne für jedes Paar i, j die Länge D[i,j] des kürzesten Pfades. durchgeführt. Naive Lösung: lasse ein SSSP-Algorithmus (z. B. Bellman-Ford) |V | mal laufen. Dies führt zu einer Worst-Case Zeitkomplexität O(|V |4 ). Floyd’s Algorithmus. 6 All Effizientere Pairs Version: Shortest Paths orithmen 17/22 yd Betrachtet wird die Länge des Pfades jedes Knotens mit jedem anderen KnoJoost-Pieter Katoen Datenstrukturen und Algorithmen 18/22 ten. Hier sind negative Kantengewichte, jedoch keine negativen Zyklen zugelassen.All-Pairs Shortest Path Der Algorithmus von Floyd Der Algorithmus von Floyd – Idee 35 6.1 Floyd-Warshall Algorithmus j .,k i Prinzip j 10 ™{ 1} Verbessert sich die Pfadlänge, wenn der Weg über Zwischenknoten führt? 1, . , k≠ } ≠1 .., Rekursionsgleichung: k≠ ,.. I Vorgehen wie bei Warshall, jedoch mit folgender Rekursionsgleichung: Y ] W (i, j) (k) 1 2 dij = [ min d (k≠1) , d (k≠1) + d (k≠1) für k > 0 ij (statt: tij (k≠1) D[i,j] = dij . (·) I 19/22 . 1 1} – Floyd-Warshall ist eine klassische k Aufgabe ™ { der Dynamischen Programmierung: für k = 0 orithmen 25 ‚ 1 ik tik (k≠1) kj · tkj (k≠1) 2 für k = 0 für k > 0 ) Auch hier arbeiten wir direkt im Ausgabearray: D[i,j] = dij . Joost-Pieter Katoen Datenstrukturen und Algorithmen 39 (·) 19/22 • Namen und Matrikelnummern der Studenten sowie die Nummer der Übungsgruppe sind auf jedes Blatt der Abgabe zu schreiben. Heften bzw. tackern Sie die Blätter! KAPITEL 7. GRAPHENALGORITHMEN Aufgabe 1 (Floyd-Algorithmus): (8 Punkte) Bestimmen Sie für den folgenden Graphen die Werte der kürzesten Pfade zwischen allen Paaren von Knoten. Nutzen Sie hierzu den Algorithmus von Floyd. Geben Sie das Distanzarray vor dem Aufruf, sowie nach jeder Iteration an. Die Knotenreihenfolge sei mit A, B, C, D, E fest vorgegeben. Beispiel 3 D A 6 3 5 8 7 7 C 4 2 9 1 E B 9 A B C D E A B C D E A B C 0 7 1 8 0 9 5 1 0 6 1 3 1 9 1 D E 3 1 1 1 1 2 0 4 7 0 A B C D 0 7 16 3 8 0 9 11 5 12 0 8 6 13 3 0 17 9 18 7 E 8 1 2 4 0 A B C D E A B C D E A B C D 0 7 1 3 8 0 9 11 5 12 0 8 6 13 3 0 1 9 1 7 A B 0 7 8 0 5 12 6 13 13 9 C 6 9 0 3 10 E 1 1 2 4 0 A B C D E A 0 8 5 6 17 B C 7 16 0 9 12 0 13 3 9 18 D E 3 7 11 1 8 2 0 4 7 0 A B C D E A B 0 7 8 0 5 11 6 13 13 9 C 6 9 0 3 10 D E 3 8 11 1 8 2 0 4 7 0 D E 3 7 8 1 8 2 0 4 7 0 . 7 Algorithmen auf Flussnetzwerken 1 7.1 Ford-Fulkerson-Methode - Maximaler Fluss Prinzip – Überführe den Graphen in ein Flussnetzwerk. – Suche einen beliebigen Pfad zwischen s und t heraus. – Bestimme die minimale Kapazität (maximalen Durchfluss) dieses Pfades. – Bestimme die Flusserhöhung, indem der Fluss entlang des Pfades gleich der Kapazität der Kante mit der minimalen Kapazität gesetzt wird. – Bilde das Restnetzwerk, indem der Durchfluss als umgekehrte Kanten eingezeichnet wird (eventuell existierende Gegenpfade werden verrechnet). 40 Maximaler Fluss Ford-Fulkerson-Methode KAPITEL 7. GRAPHENALGORITHMEN Schnitte– Fahre in Flussnetzwerken solange fort, neue Pfade zu suchen, bis es keine augmentierende Definition (den Fluss weiter vergrößernde) Pfade mehr gibt, d.h. die Senke noch von der Quelle aus erreichbar ist. → Der Maximale Fluss ist die Summe aller Flusserhöhungen. Ein Schnitt (S, T ) in einem Flussnetzwerk G = (V , E , c) ist eine Partition S fi T = V , S fl T = ? mit s œ S und t œ T . 7.2 Min-cut-max-flow I Wenn f ein Fluss in G ist, dann ist f (S, T ) der Fluss über (S, T ). I DieSKapazität c(S, ∪Schnitte T = V, S ∩von ∅(S, mitTs)∈ist S und t ∈TT ). . Die Knoten werden also in zwei inT 6=Netzwerken I Datenstrukturen und Algorithmen (Folie 366, Seite 62 im Skript) Ein Schnitt (S, T) in einem Flussnetzwerk G = (V, E, c) ist eine Partition aufgeteilt: Eine, die die Senke- und eine, welche die Quelle enthält. Ein Gruppen minimaler Schnitt ist ein Schnitt mit minimaler Kapazität. 12/12 v1 v3 11/16 s 8/13 Joost-Pieter Katoen 15/20 1/4 10 v2 4/9 11/14 7/7 v4 t 4/4 (Schnitt Der Fluß über (S, T(S) = ist/s, 19.v1 , v2 /, T = /t, v3 , v4 /) hier 26) Datenstrukturen und Algorithmen Die Kapazität von (S, T ) ist 26. Max-flow Min-cut Theorem Ist f ein Fluss im Flussnetzwerk G, dann sind äquivalent (gleichbedeutend): – f ist ein maximaler Fluss. – In Gf gibt es keinen augmentierenden Pfad. – |f | = c(S, T ) für einen Schnitt (S, T ), d.h. der Schnitt (S, T ) ist minimal. Daraus folgt: → Die Kapazität eines minimalen Schnittes ist gleich dem Wert eines maximalen Flusses. (Kapazität des minimalen Schnittes entspricht dem maximalen Flusses) → Falls die Ford–Fulkerson–Methode terminiert, berechnet sie einen maximalen Fluss. 41 37/42 8 Hashing Allgemeines Prinzip Das Ziel von Hashing ist es, einen großen Schlüsselraum auf einen kleineren Bereich von ganzen Zahlen abzubilden. Dabei soll möglichst unwahrscheinlich sein, dass zwei Schlüssel auf die selbe Zahl abgebildet werden. Eine Hashfunktion bildet einen Schlüssel auf einen Index der Hashtabelle T ab: h : U → {0, 1, ..., m − 1} für Tabellengröße m und |U | = n. Wir sagen, dass h(k) der Hashwert des Schlüssels k ist. Das Auftreten von Hashing I Grundlagen des Hashings 0 0 h (k) für k 6= k nennt man Kollision. Hashing (II) Schlüsselmenge Hashfunktion U k2 k4 K 0 Hashtabelle h(k1 ) h(k2 ) = h(k3 ) k1 Kollision h(k4 ) m≠1 benutzte Schlüssel I Das liegt am I Die Wahrscheinli Geburtstag hat is I Fragt man 23 Pe 23 365 ¥ 0,063. I Sind aber 23 Per den selben Gebur Wie finden wir Hashfunktionen, die einfach auszurechnen sind und Kollisionen minimieren? Wie behandeln wir dennoch auftretende Kollisionen? 1 Hashfunktionen Joost-Pieter Katoen Datenstrukturen und Algorithmen Hashing I Grundlagen des Hashings 1 15/35 Eine Hashfunktion bildet einen Schlüssel auf einen Index (eine ganze Zahl) Kollisionen: Das Geburtstagsparadoxon (II) ab. Gute Hashfunktionen Auf Hashing angewendet bedeutet das: I Unsere wir so Die Wahrscheinlichkeit keiner Kollision nach k Einfügevorgängen in Eine gute Hashfunktion ist charakterisiert durch folgende Eigenschaften: einer m-elementigen Tabelle ist: ≠1 m m≠1 m ≠ k + 1 kŸ m≠i · · ... · = m m m m i =0 42 I Dieses Produkt geht gegen 0. I Etwa bei m = 365 ist die Wahrscheinlichkeit für k > 50 praktisch 0. Joost-Pieter Katoen Hashing I Kollisionen: Das 1.0 Wahrscheinlichkeit keiner Kollision I Kollisionen: Das Geburtstagsparadox h(k5 ) k3 k5 Hashing I 0.8 0.6 0.4 0.2 KAPITEL 8. HASHING – Effizient – Geringer Speicheraufwand – Einfach zu berechnen – Eingabewerte müssen nur einmal ausgelesen werden – Ähnliche Schlüssel sollten zu unterschiedlichen und breit verteilten Hashwerten führen. – Die Hashfunktion sollte surjektiv sein, d.h möglichst jeder Hashwert sollte genutzt werden. – Dabei sollte alle Indizes in etwa die gleiche Häufigkeit besitzen. Es gibt verschiedene Methoden, um eine gute Hashfunktion zu erhalten: – Divisionsmethode: Hashfunktion: h(k) = k mod m – Dabei sollte m möglichst eine Primzahl sein und nicht zu nahe an einer Zweierpotenz liegen. – Multiplikationsmethode: Hashfunktion: h(k) = bm(k · c mod 1)c für 0 < c < 1 k ·c mod 1 ist hierbei der Nachkommateil von k ·c, d.h. k ·c−bk ·cc. – Universelles Hashing: Löst das Problem, dass es immer eine ungünstige Sequenz von Schlüsseln geben kann, sodass alle Schlüssel auf einen Hashwert abgebildet werden. – Dazu wird beim universellen Hashing eine zufällige Hashfunktion aus einem Pool H von Hashfunktionen ausgewählt. – Eine Menge H der Hashfunktionen wird als universell betrachtet, wenn mit einer zufällig aus H ausgewählten Hashfunktion die Wahrscheinlichkeit für eine Kollision zwischen den Schlüsseln k und l nicht größer als die Wahrscheinlichkeit m1 einer Kollision ist, wenn h(k) und h(l) zufällig und unabhängig aus der Menge {0, 1, ..., m − 1} gewählt wurden. – Für universelles Hashing ist die erwartete Länge der Liste T [k] - gleich α, wenn k nicht in T enthalten ist. - gleich 1 + α, wenn k in T enthalten ist. 2 Geschlossenes Hashing Beim geschlossenen Hashing, auch Kollisionsauflösung durch Verkettung genannt, werden alle Schlüssel, die zum gleichen Hashwert führen, innerhalb einer verketteten Liste gespeichert, auf die ein Arrayeintrag verweist: 43 hmen Kollisionsauflösung durch Verkettung (I) Idee KAPITEL 8. führen, HASHING Alle Schlüssel, die zum gleichen Hash werden in einer verketteten Liste gespeichert. k2 k4 k1 k7 k3 k 6 k5 k1 k2 k3 k6 k7 k5 k4 m≠1 19/35 II) Joost-Pieter Katoen Datenstrukturen und Algorithmen Hashing I Verkettung Idee der Kollisionsauflösung durch Verkettung 20/35 Kollisionsauflösung durch Verkettung (III) Komplexität – Im Worst-Case Worst-Case haben alle Schlüssel den selben Hashwert. Suchen und Komplexität Löschen hat die selbe Worst-Case wieetwa bei⇥(1). Listen: W (n) = Angenommen, die Berechnung von h(k)Komplexität ist recht effizient, Θ(n). Die Komplexität ist: Suche: Proportional zurbeträgt Länge derdie Liste T [h(k)]. zum (erfolgreichen und – Im Average-Case jedoch Laufzeit Einfügen:Suchen Konstant (ohne=Überprüfung, erfolglosen) A(n) Θ(1 + α). ob das Element schon vorhanden ist). n (n: Anzahl der eingeα ist hierbei der Füllgrad der Hashtabelle: α = m Löschen: Proportional zur Länge der Liste T [h(k)]. gebenen Schlüssel, m: Größe der Hashtabelle) Schlüssel k in der fang der Liste Liste T[h(e.key)]. hmen 0 U K [Luhn 1953] I Im Worst-Case haben alle Schüssel den selben Hashwert. I Suche und Löschen hat dann die selbe Worst-Case Komplexität wie Listen: ⇥(n). I Im Average-Case ist Hashing mit Verkettung aber dennoch effizient! 3 Offenes Hashing 21/35 Joost-Pieter Katoen Allgemeines Prinzip: Datenstrukturen und Algorithmen 22/35 Beim offenen Hashing, auch Hashing mit offener Adressierung genannt, werden alle Schlüssel direkt auf eine einzige Hashtabelle abgebildet. Dabei können höchstens n Schlüssel gespeichert werden: α(n, m) = n ≤1 m Einfügen eines Schlüssels k – Sondiere die Positionen der Hashtabelle, bis ein leerer Slot gefunden wird – Die zu überprüfenden Positionen werden mittels einer Sondierungsfunktion in Abhängigkeit des Schlüssels k bestimmt. 44 KAPITEL 8. HASHING Die Hashfunktion hängt also sowohl vom Schlüssel k, als auch von der Nummer der Sondierung i ab. Sondierungsfunktionen Sondierungsfunktionen ordnen einen Schlüssel k auf eine Position der Hashtabelle zu. Dazu wird bei jeder fehlgeschlagenen Zuordnung eines Schlüssels dessen Zähler i um 1 hochgesetzt und die Sondierungsfunktion mit diesen modifizierten Parametern erneut aufgerufen. – Lineares Sondieren: h(k, i) = (h0 (k) + i) mod m (für i < m) mit: k: Schlüssel i: Sondierungsschritt h’: Hashfunktion – Die Verschiebung der nachfolgende Sondierungen ist linear von i abhängig, die erste Sondierung bestimmt bereits die gesamte Sequenz. → Es können insgesamt m verschiedene Sequenzen erzeugt werden. – Quadratisches Sondieren: h(k, i) = (H 0 (k) + c1 · i + c2 · i2 ) mod m (für i < m) mit: k: Schlüssel i: Sondierungsschritt h’: Hashfunktion Konstanten c1 , c2 6= 0 – Die Verschiebung der nachfolgende Sondierungen ist quadratisch von i abhängig, die erste Sondierung bestimmt auch hier bereits die gesamte Sequenz. → Bei geeignet gewähltem c1 , c2 können auch hier insgesamt m verschiedene Sequenzen erzeugt werden. → Clustering (Aneinanderreihung), welches bei linearem Sondieren auftritt wird jedoch vermieden. Jedoch tritt sekundäres Clustering immer noch auf: Beispiel: h(k, 0) = h(k 0 , 0) verursacht h(k, i) = h(k 0 , i) (für alle i) – Doppeltes Hashing: h(k, i) = (h1 (k) + i · h2 (k)) mod m (für i < m) mit: h1 , h2 : Übliche Hashfunktionen. 45 20/1 22/1 h1 (k) = k mod 11 h2 (k) = 1 + k mod 10 h(k, i) = (h1 (k) + i · h2 (k)) mod 11 KAPITEL 8. HASHING Joost-Pieter Katoen Hashing II Datenstrukturen und Algorithmen 21/1 - Die Verschiebung der nachfolgenden Sondierungen ist von h2 (k) abhängig. Die erste Sondierung bestimmt also nicht die gesamte Offene Adressierung Sequenz. Dies führt zu einer besseren Verteilung der Schlüssel in der Praktische→Effizienz von Doppeltem Hashing Hashtabelle. Dies kommt gleichverteiltem Hashing nahe. I I Hashtabelle- mit 051 m Einträgen (Endfüllgrad 99,95%) 358 Sind538 h2 und relativ prim (Teilerfremd), wird99,8 die% -> gesamte Hashtabelle abgesucht. Mittlere Anzahl Kollisionen ÷ pro Einfügen in die Hashtabelle: 14 Lineares Sondieren Quadratisches Sondieren Doppeltes Hashing 12 10 ÷ 8 6 4 2 0 0 10 20 30 40 50 60 70 80 90 100 Füllgrad der Hashtabelle (in %) Vergleich der drei Sondierungsmethoden Joost-Pieter Katoen Datenstrukturen und Algorithmen 23/1 Komplexität Average-Case: – Die erfolgreiche Suche benötigt O 1 α 1 – Die erfolglose Suche benötigt O( 1−α ) 1 · ln 1−α Löschen eines Schlüssels k Problematik Im Gegensatz zum Geschlossenen Hashing kann ein zu löschender Schlüssel hier nicht einfach gelöscht (durch null ersetzt) werden, da ansonsten das beim Einfügen geschehene Sondieren nicht berücksichtigt wird. Daher muss hier statt einem Löschen (ersetzen mit null ) der Schlüssel durch einen speziellen Wert DELETED ersetzt werden. 46 9 Algorithmische Geometrie Algorithmische Geometrie Algorithmische Geometrie Winkelbestimmung 1 Mathematische(III) Grundlagen Problem Winkelbestimmung mit Determinanten Gegeben der Streckenzug (p, q, r). Wird bei q nach links oder rechts Gegeben der Streckenzug (p, q, r): ]pqr > 180¶ oder < 180¶ ? abgebogen? Oder: Ist der Winkel qr r x2 q pq p I I I I ˛b ˛a – ]pqr = 180¶ +– x1 − → → − Determinante. − → − Wir verwenden wieder – → a = dpq = q − p und die b = dpr = r − q −q ≠ p und ˛ Dazu berechnen wir ˛a = ˛dpq→ =→ b = ˛dqr = r ≠ q. – Linksdrehung, wenn det(− a, b)>0 det(˛a, ˛b) > 0, falls der Knick→ nach → − links geht (]pqr > 180¶ , – > 0). − – Rechtsdrehung, wenn det( a , b ) < 0 Wenn r auf (der a, ˛b) =◦ 0 → −Verlängerung von) pq liegt, dann ist det(˛ → − – Wenn det( a , b ) = 0 liegt (]pqr = 0¶◦ oder = 180¶ ). r auf der Verlängerung von pq. (]pqr = 0 oder 180 ) Joost-Pieter Katoen Datenstrukturen und Algorithmen Algorithmische Geometrie Konvexe Hülle Polygone Polygone Algorithmische Geometrie Algorithmische Geometrie Schnitt zweier Strecken Problem Gegeben zwei Strecken pq und rs. Schneiden sich diese? nicht einfach einfach nicht konvex konvex, einfach q r Ein Polygon heißt einfach, wenn es sich nicht selbst schneidet. konvex 26/33 p 47 s Ein Polygon heißt konvex, wenn jede Verbindung (Konvexkombination) zweier Punkte des Polygons nie außerhalb des Polygons liegt. I Wir sind nicht an der Position des Schnittpunktes interessiert. I Idee:Joost-Pieter Wir Katoen testen, ob die EndpunkteDatenstrukturen r und sundauf verschiedenen 27/33 Seiten Algorithmen 10/33 Joost-Pieter Katoen Datenstrukturen und Algorithmen KAPITEL 9. ALGORITHMISCHE GEOMETRIE Algorithmische Geometrie 26/33 Konvexe Hülle Konvexe Hülle Konvexe Hülle p11 p6 p5 p12 p0 p4 p1 p8 p2 p3 p9 p7 p10 Konvexe Hülle konvexeHülle Hülle einer einer Menge Punkten ist das konvexe PoDieDie konvexe MengeQQvonvon Punkten ist kleinste das kleinste konvexe lygon P, für das sich jeder Punkt in Q entweder auf dem Rand von P oder in Polygon P, für das sich jeder Punkt in Q entweder auf dem Rand von P seinem Inneren befindet. Hilfreich ist hierbei die Vorstellung der Punktemenoder Inneren befindet. ge in als seinem Nägel auf einem Brett, um die außen ein Gummiband gespannt wird. Die konvexe Hülle hat dann die Form, die durch das straffe Gummiband Betrachte jeden als Nagel, der aus einem Brett herausragt. gebildet wird, das allePunkt Nägel umschließt. I I Die konvexe Hülle hat dann die Form, die durch ein straffes Gummiband gebildet wird, das alle Nägel umschließt. 2 Graham-Scan Joost-Pieter Katoen Datenstrukturen und Algorithmen Der Graham-Scan gibt die konvexe Hülle einer Menge Q von Punkten in einem zweidimensionalen Raum aus. Prinzip 1. Suche den Startpunkt, der auf jeden Fall auf der Hülle liegt. – Konvention: Gewählt wird der Punkt mit der niedrigsten x2 - Koordinate. Haben mehrere Punkte die gleiche x2 -Koordinate, so GIBT zusätzlich die geringste x1 -Koordinate den Ausschlag 2. Von Punkt diesem ausgehend sortiere die Punkte nach zunehmendem (Polar-) Winkel zur x1 - Achse (entspricht einer rotierenden Sweepline): – Dies geschieht mittels Determinantenberechnung: Die Determinanten der Strecken vom Startpunkt zum betrachteten Punkt und der x1 -Achse werden berechnet. Die Reihenfolge der Determinanten gibt die Polarwinkelreihenfolge an. 48 28/33 KAPITEL 9. ALGORITHMISCHE GEOMETRIE ülle Algorithmische Geometrie – Beispiel Konvexe Hülle Konvexe Hülle – Graham-Scan – Beispiel p9 p6 p8 p7 p2 p8 p9 p3 p5 p4 p3 p2 p7 p1 p0 uren und Algorithmen 30/33 Joost-Pieter Katoen Datenstrukturen und Algorithmen 30/33 3. Die ersten drei Punkte werden nun auf einen Stapel gelegt (und somit als ’aufKonvexe der konvexen liegend’ betrachtet). Hülle –Hülle Graham-Scan – Beispiel ülle Algorithmische Geometrie – Beispiel Konvexe Hülle 4. Die weiteren Punkte werden einzeln auf ihren Winkel zu den beiden p9 Vorgängerpunkten auf dem Stapel überprüft: – Die Winkelüberprüfung geschieht mittels Determinante: p5 p6 p10 p5 p7 det(stack.top − (stack.top − 1), N euesElement − stack.top) p4 p8 p3 p2 uren und Algorithmen p4 p 3 p11 die Determinante von (oberstem Stackelement ab→ Es pwird also 12 züglich zweitoberstem Stackelement) und (neu hinzukommendem Element abzüglich oberstem Stackelement) berechnet. p2 p1 p1 – Hier sind zwei Fälle möglich: 30/33 p0 a) Ist die berechnete Determinante ≤ 0, knickt die Verbindungsstrecke der drei Punkte bei Stack.top nach rechts ab. Das oberste Stapelelement wird entfernt, da es sich nicht auf der Hülle, sondern im inneren des Polygons befindet. Joost-Pieter Katoen Datenstrukturen und Algorithmen 30/33 – Das neu hinzukommende Element wird nun erneut mit den restlichen auf dem Stack befindlichen Elementen überprüft. b) Ist die berechnete Determinante > 0, knickt die Verbindungsstrecke der drei Punkte bei Stack.top nach links ab. Das neu hinzukommende Element wird oben auf den bestehenden Stapel gelegt. 49 KAPITEL 9. ALGORITHMISCHE GEOMETRIE Joost-Pieter Katoen Datenstrukturen und Algorithmen Algorithmische Geometrie 30/33 Joost-Pieter Katoen Datenstrukturen und Algorithmen Konvexe Hülle lle p9 p9 p6 p6 p5 p7 p8 p7 p4 p8 p3 p2 p5 p4 p3 p2 p1 p4 p2 p1 Datenstrukturen und Algorithmen 30/33 Joos uren und Algorithmen Rot: p7 wird aus konvexer Hülle entfernt, Grün: p8 wird anschließend überprüft. Beispiel 2 Konvexe Hülle für folgende Punkte in einem Zweidimensionalen KoordinaDatenstrukturen und Algorithmen SoSe 2012, Fragestunde 29.08.2012 tensystem: Lösung (1, 0); (4, 1); (4, 3); (3, 3); (2, 2); (1, 4); (0, 2) Knoten Vektoren und Determinante (3, 3) ✓ ◆ ✓ ◆ 0 1 det( , )=0+2=2>0 2 0 (4, 3) (4, 1) (2, 2) (3, 3) (4, 3) (1, 4) (2, 2) (3, 3) ✓ det( ✓ det( 1 0 1 1 ◆ ✓ , ◆ ✓ , 1 2 1 1 ◆ ◆ )= )=1 Stack 0=1>0 2 1= 30 1 0= 10 (3, 3) (4, 3) (4, 1) (1, 0) (2, 2) (3, 3) (4, 3) (4, 1) (1, 0) (3, 3) (4, 3) (4, 1) (1, 0) 50 (1, 4) (3, 3) (4, 3) p p0 p0 Joost-Pieter Katoen p5 ✓ det( 1 0 ◆ ✓ , 2 1 ◆ )= (4, 3) (4, 1) p1 (2, 2) (3, 3) ✓ det( Knoten (1, 4) (2, 2) (3, 3) (1, 4) (3, 3) (4, 3) (1, 4) (4, 3) (4, 1) (0, 2) (1, 4) (4, 3) ◆ ✓ , ◆ 1 1 KAPITEL 9. ALGORITHMISCHE GEOMETRIE 0 1 )=1 0=1>0 Vektoren und Determinante Stack ✓ det( 1 1 ◆ ✓ , 1 2 ◆ 2 1= 30 ✓ det( 1 0 ◆ ✓ , 2 1 ◆ 1 0= 10 )= )= ✓ ◆ ✓ ◆ 0 3 det( , )=0+6=6>0 2 1 ✓ det( 3 1 ◆ ✓ , 1 2 4 51 ◆ (2, 2) (3, 3) (4, 3) )=6+1=7>0 (3, 3) (4, 3) (4, 1) (1, 0) (4, 3) (4, 1) (1, 0) (1, 4) (4, 3) (4, 1) (1, 0) (0, 2) (1, 4) (4, 3) (4, 1) (1, 0)