650 likes | 748 Views
Vorlesung Informatik 2 Algorithmen und Datenstrukturen (25 – Fibonacci-Heaps – Analyse). T. Lauer. Fibonacci-Heaps: Idee.
E N D
Vorlesung Informatik 2Algorithmen und Datenstrukturen(25 – Fibonacci-Heaps – Analyse) T. Lauer
Fibonacci-Heaps: Idee • Liste von Bäumen (beliebigen Verzweigungsgrades), die alle heapgeordnet sind.Definition:Ein Baum heißt (min-)heapgeordnet, wenn der Schlüssel jedes Knotens größer oder gleich dem Schlüssel seines Vaterknotens ist (sofern er einen Vater hat). • Die Wurzeln der Bäume sind in einer doppelt verketteten, zirkulären Liste miteinander verbunden (Wurzelliste). • Der Einstiegspunkt ist ein Zeiger auf den Knoten mit minimalem Schlüssel. 7 2 19 11 13 45 8 24 15 36 21 83 52 79
Fibonacci-Heaps: Knotenformat class FibNode { Object content; // der eigentliche Inhalt int key; // Schlüssel (Priorität) FibNode parent, child; // Zeiger auf Vater und einen Sohn FibNode left, right; // Zeiger auf linken und rechten Nachbarn intrank; // Anzahl der Söhne dieses Knotens booleanmark; // Markierung } Die Zahlrankgibt an, wie viele Söhne der Knoten hat (= der Rang des Knotens) Die Bedeutung der Markierungmarkwird später deutlich. Diese Markierung gibt an, ob der Knoten bereits einmal einen seiner Söhne verloren hat, seitdem er selbst zuletzt Sohn eines anderen Knotens geworden ist.
Fibonacci-Heap: Detaillierte Darstellung 7 2 19 11 13 45 8 24 15 36 21 83 52 79
Fibonacci-Heaps: Operationen • Q.accessmin(): Gib den KnotenQ.minzurück (bzw. null, wenn Q leer ist). • Q.insert(int k): Erzeuge einen neuen Knoten Nmit Schlüssel k und füge ihn in die Wurzelliste von Q ein. Fallsk < Q.min.key, aktualisiere den Minimum-Zeiger (setzeQ.min = N). Gib den neu erzeugten Knoten zurück. 7 6 19 11 13 45 8 24 15 36 21 83 52 79
Fibonacci-Heaps: Operationen • Q.accessmin(): Gib den KnotenQ.minzurück (bzw. null, wenn Q leer ist). • Q.insert(int k): Erzeuge einen neuen Knoten Nmit Schlüssel k und füge ihn in die Wurzelliste von Q ein. Fallsk < Q.min.key, aktualisiere den Minimum-Zeiger (setzeQ.min = N). Gib den neu erzeugten Knoten zurück. 7 6 4 19 11 13 45 8 24 15 36 21 83 52 79
Fibonacci-Heaps: Operationen • Q.accessmin(): Gib den KnotenQ.minzurück (bzw. null, wenn Q leer ist). • Q.insert(int k): Erzeuge einen neuen Knoten Nmit Schlüssel k und füge ihn in die Wurzelliste von Q ein. Fallsk < Q.min.key, aktualisiere den Minimum-Zeiger (setzeQ.min = N). Gib den neu erzeugten Knoten zurück. 7 6 4 19 11 13 45 8 24 15 36 21 83 52 79
Manipulation von Bäumen in Fibonacci-Heaps Drei Basis-Methoden zur Manipulation von Bäumen in Fibonacci-Heaps: link: „Wachstum“ von Bäumen.Zwei Bäume werden zu einem neuen verbunden. cut: „Beschneiden“ von Bäumen im Inneren. Ein Teilbaum wird aus einem Baum herausgetrennt und als neuer Baum in die Wurzelliste eingefügt. remove: „Spalten“ von Bäumen an der Wurzel. Entfernt die Wurzel eines Baums und fügt die Söhne der Wurzel als neue Bäume in die Wurzelliste ein.
Baummanipulation: link link: Input: 2 Knoten mit demselben Rang k in der Wurzelliste Methode: vereinigt zwei Bäume mit gleichem Rang, indem die Wurzel mit größerem Schlüssel zu einem neuen Sohn der Wurzel mit kleinerem Schlüssel gemacht wird.Die Gesamtzahl der Bäume verringert sich um 1, die Knotenzahl ändert sich nicht. Output: 1 Knoten mit Rang k+1 Kosten: O(1) 2 24 2 83 52 13 8 13 8 24 36 21 36 21 83 52
Baummanipulation: cut cut: Input: 1 Knoten, der nicht in der Wurzelliste ist Methode: trennt den Knoten (samt dem Teilbaum, dessen Wurzel er ist) von seinem Vater ab und fügt ihn als neuen Baum in die Wurzelliste ein. Die Gesamtzahl der Bäume erhöht sich um 1, die Knotenzahl ändert sich nicht. Kosten :O(1) 2 2 21 13 45 8 13 45 8 26 15 36 21 15 36 26
Baummanipulation: remove remove: Input: 1 Knoten mit Rank k aus der Wurzelliste Methode: entfernt die Wurzel eines Baums und fügt stattdessen seine k Söhne in die Wurzelliste ein. Die Zahl der Bäume erhöht sich um k-1, die Gesamtzahl der Knoten verringert sich um 1. Kosten: O(1) [sofern die Vaterzeiger der Söhne nicht gelöscht werden!] 2 13 45 8 15 36 21 13 45 8 15 36 21 26 26
Entfernen des Minimums Q.deletemin(): • Wenn Q leer ist, gib null zurück. • Andernfalls: • Entferne den Minimalknoten (mit remove). • „Konsolidiere“ die Wurzelliste:Verbinde (mit link) je zwei Wurzelknoten mit demselben Rang, und zwar solange, bis nur noch Knoten mit unterschiedlichem Rang in der Wurzelliste vorkommen.Lösche dabei die Markierungen der Knoten, die zum Sohn eines anderen werden, und entferne außerdem evtl. vorhandene Vaterzeiger der Wurzelknoten. • Finde unter den Wurzelknoten das neuen Minimum. • Gib den entfernten Knoten zurück.
deletemin: Beispiel 7 2 19 11 13 45 8 24 15 36 21 83 52
consolidate: Beispiel Rang-Array: 0 1 2 3 4 5 7 13 45 8 19 11 15 36 21 24 83 52
consolidate: Beispiel Rang-Array: 0 1 2 3 4 5 7 13 45 8 19 11 15 36 21 24 83 52
consolidate: Beispiel Rang-Array: 0 1 2 3 4 5 7 13 45 8 11 15 36 21 19 24 83 52
consolidate: Beispiel Rang-Array: 0 1 2 3 4 5 7 45 8 11 36 21 13 15 19 24 83 52
consolidate: Beispiel Rang-Array: 0 1 2 3 4 5 7 8 11 36 21 13 45 15 19 24 83 52
consolidate: Beispiel 7 8 11 36 21 13 45 15 19 24 83 52
Laufzeitanalyse Laufzeit von deletemin(): remove: O(1) consolidate: O(#links) + O(maxRank(n)) updatemin: O(#Wurzelknoten nach consolidate) = O(maxRank(n)) Nach dem Konsolidieren gibt es von jedem Rang nur noch höchstens einen Wurzelknoten. Definiere maxRank(n) als den höchstmöglichen Rang, den ein Wurzelknoten in einem Fibonacci-Heap der Größe n haben kann. (Berechnung von maxRank(n)später.)
Analyse von consolidate rankArray = new FibNode[maxRank(n)+1]; // Erstelle das Array for „each FibNode N in rootlist“ { while (rankArray[N.rank] != null) { // Position besetzt N = link(N, rankArray[N.rank]); // Verbinde Bäume rankArray[N.rank-1] = null; // Lösche alte Pos. } rankArray[N.rank] = N; // Eintragen in Array } Die Gesamtkosten von consolidate werden dominiert von der Anzahl der link-Operationen und der Anzahl der Array-Modifikationen.
Gesamtkosten von deletemin remove: O(1) Erstellen des Rang-Arrays: O(maxRank(n)) for-Schleife: O(#links)+O(maxRank(n)) Update des Minimum-Zeigers: O(maxRank(n)) Gesamtkosten: O(#links) + O(maxRank(n))
Herabsetzen eines Schlüssels Q.decreasekey(FibNode N, int k): • Setze den Schlüsselwert vonNaufkherab. • Wenn die Heap-Bedingung nicht mehr erfüllt ist (k < N.parent.key): • TrenneNvon seinem Vater ab (mit cut) • Falls der Vater markiert ist (N.parent.mark == true), trenne auch ihnvon seinem Vater ab; wenn auch dessen Vater markiert ist, trenne auch diesen ab, usw. („cascading cuts“) • Markiere den Knoten, dessen Sohn zuletzt abgetrennt wurde (sofern dieser kein Wurzelknoten ist). • Aktualisiere den Minimum-Zeiger (falls k < min.key).
Beispiel für decreasekey 6 7 5 11 13 45 8 64 24 15 36 21 83 52 64 Setze den Schlüssel 64 auf 14 herab.
Beispiel für decreasekey 6 7 5 11 13 45 8 64 24 15 36 21 83 52 14
Beispiel für decreasekey 6 7 5 14 11 13 45 8 64 24 15 36 21 83 52
Beispiel für decreasekey 6 7 5 21 14 11 13 45 8 64 24 15 83 52 36
Beispiel für decreasekey 6 7 5 21 14 11 13 45 8 64 24 15 83 52 36
Laufzeitanalyse Laufzeit von decreasekey(): Schlüssel neu setzen: O(1) cut: O(1) Cascading cuts: #cuts ·O(1) Markieren: O(1) Gesamtkosten: O(#cuts)
Entfernen eines Knotens Q.delete(FibNode N): • FallsN == Q.min, führeQ.deletemin() aus. • Andernfalls: • Falls N nicht in der Wurzelliste ist: • Trenne N von seinem Vater ab (mit cut) • Falls der Vater markiert ist (N.parent.mark == true), trenne auch ihn von seinem Vater ab; wenn auch dessen Vater markiert ist, trenne auch diesen ab, usw. („cascading cuts“) • Markiere den Knoten, dessen Sohn zuletzt abgetrennt wurde (sofern dieser kein Wurzelknoten ist). • Entferne N aus der Wurzelliste (mit remove).
Beispiel für delete 6 7 5 11 13 45 8 64 24 15 36 21 83 52 64 Lösche den Knoten mit Schlüssel 21.
Beispiel für delete 6 7 5 11 13 45 8 64 24 15 36 21 83 52 64
Beispiel für delete 6 7 5 21 11 13 45 8 64 24 64 15 83 52 36
Beispiel für delete 6 7 5 21 11 13 45 8 64 24 64 15 83 52 36
Beispiel für delete 6 7 5 8 21 11 13 45 64 24 36 64 15 83 52
Beispiel für delete 21 6 7 5 8 11 64 13 45 64 24 36 15 83 52
Laufzeitanalyse Laufzeit von delete(): • Falls der entfernte Knoten der Minimalknoten ist, sind die Kosten gleich wie bei deletemin: O(#links) + O(maxRank(n)) • Ansonsten sind sie ähnlich wie bei decreasekey: cut: O(1) Cascading cuts: #cuts ·O(1) Markieren: O(1) remove: O(1) Gesamtkosten: O(#cuts)
Analyse Beobachtungen:Bei deletemin beeinflusst die Zahl der link-Operationen die tatsächliche Laufzeit.Bei decreasekey (und delete) ist es die Zahl der cascading cuts. In beiden Fällen bekommen wir im schlimmsten Fall eine lineare Laufzeit: Aber: Wie häufig kann das passieren? 7 5 8 21 2 5 11 28
Amortisierte Analyse Beobachtungen:Bei deletemin beeinflusst die Zahl der link-Operationen die tatsächliche Laufzeit.Bei decreasekey (und delete) ist es die Zahl der cascading cuts. Idee: Spare dafür Guthaben an (Bankkonto-Paradigma)!Annahme: Die Kosten pro link und pro cut seien jeweils 1€. • Sorge dafür, dass für jeden Wurzelknoten immer 1€ Guthaben vorhanden ist, mit dem sich die link-Operation bezahlen lässt, wenn dieser Knoten an einen anderen angehängt wird. • Sorge dafür, dass für jeden markierten Knoten, der nicht in der Wurzelliste ist, immer 2€ Guthaben vorhanden sind. Damit kann man bei „cascading cuts“ das Abtrennen (cut) dieses Knotens bezahlen und hat noch 1€ übrig für (1).
Beispiel 7 13 45 3 9 10 36 21 14 32 47 11 24 61 50 39 36 52 75 79
Guthaben ansparen Bei welchen Operationen müssen wir etwas dazu bezahlen? Neue Wurzelknoten können entstehen bei: • insert: gib dem neu eingefügten Wurzelknoten noch 1€ dazu. • decreasekey: bezahle1€ für den abgetrennten Knoten dazu. • deletemin und delete: bezahle bei remove für jeden Sohn des entfernten Knotens 1€ dazu, insgesamt also bis zu maxRank(n) €. Markierte Knoten können nur am Ende von decreasekey und delete entstehen: • Bezahle beim Markieren zusätzlich 2€ für den markierten Knoten.
Amortisierte Kosten von insert Erstellen des Knotens: O(1) Einfügen in Wurzelliste: O(1) + O(1) Amortisierte Gesamtkosten: O(1)
Amortisierte Kosten von deletemin remove: O(1) + O(maxRank(n)) Erstellen des Rang-Arrays: O(maxRank(n)) link-Operationen: #links ·O(1) wird vom Guthaben bezahlt! Restl. Eintragungen: O(maxRank(n)) Update Minimum-Zeiger: O(maxRank(n)) Amortisierte Gesamtkosten: O(maxRank(n))
Beispiel 7 13 45 3 9 10 36 21 14 32 47 11 24 61 50 39 36 52 75 79
Beispiel 7 13 45 36 21 9 10 14 32 47 11 24 61 50 39 36 52 75 79 remove: Bezahle zusätzlich für jeden Sohn 1€:
Beispiel 7 13 45 21 9 10 14 32 36 47 11 24 61 50 39 36 52 75 79 link: Bezahle alle Kosten mit dem Guthaben.
Beispiel 7 13 45 9 10 14 32 47 11 24 61 21 50 39 36 52 36 75 79 link: Bezahle alle Kosten mit dem Guthaben.
Beispiel 7 13 9 10 45 14 32 47 11 24 61 21 50 39 36 52 36 75 79 link: Bezahle alle Kosten mit dem Guthaben.
Beispiel 7 9 10 45 47 11 24 61 21 13 36 52 36 14 32 79 50 39 75 link: Bezahle alle Kosten mit dem Guthaben.
Beispiel 7 9 45 47 11 24 10 36 52 61 21 13 79 36 14 32 50 39 75 link: Bezahle alle Kosten mit dem Guthaben.