Datenstrukturen und Algorithmen Vorlesung 8
Inhaltsverzeichnis Vorige Woche: ADT Stack ADT Queue Heute betrachten wir: ADT Deque ADT Prioritätsschlange Binomial-Heap
Schriftliche Prüfung Informationen Die schriftliche Prüfung findet in der ersten Hälfte aus Seminar 5 statt Dauer: 50 Minuten Alle nehmen mir ihrer Gruppe an der Prüfung Teil! (wie im Stundenplan)
Schriftliche Prüfung Aufgaben Die Aufgaben sind der Form: Container + Repräsentierung (Datenstruktur) + Operation Containers: Bag/MultiSet Set/Menge MultiMap List Sortierte Containers für die oben genannten Containers Schwachbesetzte Matrix (sparse matrix)
Schriftliche Prüfung Aufgaben Datenstrukturen: Einfach verkettete Listen mit dynamischer Allokation Doppelt verkettete Listen mit dynamischer Allokation Einfach verkettete Liste auf Arrays Doppelt verkettete Listen auf Arrays Operationen: Einfügen (add) Löschen (remove) Suchen (search) Für schwachbesetzte Matrix: Wert ändern (modify), Element von der Position (i,j) suchen
Schriftliche Prüfung Aufgaben Beispiel: ADT Set repräsentiert auf doppelt verkettete Liste auf Array Operation: add
Schriftliche Prüfung Punkteanzahl 1p Startnote 0.5p Spezifikation der Operation: Header, Vor- und Nachbedingungen add(s, e) pre: s S, e TElem post: s S, s = s e (falls e in s enthalten ist, dann wird e nicht nochmal eingefügt) 0.5p Beschreibung des Containers Ein Set/Menge ist ein Container, deren Elemente eindeutig sind, wobei die Reihenfolge der Elemente keine Rolle spielt (die Elemente haben keine entsprechende Positionen)
Schriftliche Prüfung Punkteanzahl 1.25p Repräsentierung 1p Strukturen, die gebraucht werden um den Container zu repräsentieren 0.25p Struktur des Iterators für den Container Set: cap: Integer elems: TElem[] next: Integer[] prev: Integer[] head: Integer tail: Integer firstempty: Integer Iterator: set: Set current: Integer
Schriftliche Prüfung Punkteanzahl 4.5p Implementierung der Operation in Pseudocode Bei einer Datenstruktur mit dynamischer Allokation kann man die Operationen allocate und deallocate/free benutzen. Alle anderen Funktionen, die ihr in der Implementierung benutzen wollt, müsst ihr auch implementieren. Bei einer Datenstruktur auf Arrays braucht ihr kein Code für die Vergrößerungsoperation (resize) zu schreiben, aber ihr müsst zeigen wo und wann diese stattfinden muss:... if s.firstempty = -1 then @resize end-if
Schriftliche Prüfung Punkteanzahl 1.25p Komplexität 0.25p Bester Fall mit Erklärungen 0.5p Schlimmster Fall mit Berechnung und Erklärungen 0.5p Durchschnittlicher Fall mit Berechnung 1p Stil Generisch (mit TElem, TComp, generische Relation) Ist es effizient? Usw.
ADT Deque ADT Deque (Double Ended Queue) ist ein Container, wo die Daten an beiden Enden gelesen, eingefügt oder entfernt werden können: push_front und push_back pop_front und pop_back top_front und top_back Man kann ein Stack oder eine Schlange mit einer Deque simulieren, wenn man nicht alle Operationen braucht
ADT Deque Mögliche Repräsentierungen für Deque: Zyrkuläres Array Doppelt verkettete Liste Dynamisches Array von Arrays mit konstanter Größe
ADT Deque - Repräsentierung Eine interessante Repräsentierung für Deque ist ein dynamisches Array von Arrays mit konstanter Kapazität: Man speichert die Elemente in Arrays mit konstanter Kapazität (Blocks) In einem dynamischen Array speichert man die Adressen der Blocks Alle Blocks außer dem ersten und dem letzten sind voll Der erste Block wir von rechts nach links ausgefüllt Der letzte Block wird von links nach rechts ausgefüllt Falls der erste oder letzte Block voll ist, dann erstellt man einen neuen Block und man speichert die Adresse in dem dynamischen Array Falls das dynamische Array voll ist, dann wird seine Kapazität vergrößert (man allokiert einen größeren Speicherplatz und die Adressen der Blocks werden kopiert, aber nicht geändert)
Deque - Beispiel 76 19 32 99 34 45 56 34 23 57 12 14 45 65 54 23 46 Die Elemente der Deque sind: 76, 19, 45,..., 12, 14,..., 46, 32, 99, 34
Deque - Beispiel Informationen, die man speichern muss für die Repräsentierung auf ein dynamisches Array von Blocks: Blockgröße Das dynamische Array von Blockadressen Die Kapazität des dynamischen Arrays Die erste besetzte Position in dem dynamischen Array Die erste besetzte Position in dem ersten Block Die letzte besetzte Position in dem dynamischen Array Die letzte besetzte Position in dem letzten Block Die letzten zwei Felder müssen nicht unbedingt gespeichert werden, wenn man die Anzahl der Elemente aus dem Deque kennt
ADT Prioritätsschlange/Vorrangwarteschlange (Priority Queue) ADT Prioritätsschlange ist ein Container, in welchem jedes Element eine zugewiesene Priorität hat In einer Prioritätsschlange hat man Zugriff nur auf das Element mit der höchsten Priorität Es gilt also das HPF Prinzip (Highest Priority First)
ADT Prioritätsschlange Generell, kann man die Relation R auf die Menge der Prioritäten definieren: R : TPriority TPriority Das Element mit der höchsten Priorität wird von der Relation R bestimmt Zum Beispiel, falls die Relation R =, dann ist das Element mit der höchsten Priorität das größte Element (Maximum)
ADT Prioritätsschlange Domäne: PQ = { pq pq ist eine Prioritätsschlange mit Elementen (e, p), e TElem, p TPriority }
ADT Prioritätsschlange - Interface init(pq, R) descr: erstellt eine leere Prioritätsschlange pre: R ist eine Relation auf die Menge der Prioritäten, R : TPriority TPriority post: pq PQ, pq ist eine leere Prioritätsschlange destroy(pq) descr: zerstört eine Prioritätsschlange pre: pq PQ post: pq wurde zerstört
ADT Prioritätsschlange - Interface push(pq, e, p) descr: fügt ein neues Element in die Prioritätsschlange ein pre: pq PQ, e TElem, p TPriority post: pq PQ, pq = pq (e,p) pop(pq, e, p) descr: liefert das Element mit der höchsten Priorität aus der Prioritätsschlange und entfernt es von der Schlange. Die Priorität des Elementes wird auch zurückgegeben. pre: pq PQ post: e TElem, p TPriority, e ist das Element mit der höchsten Priorität aus pq und p ist seine Priorität, pq PQ, pq = pq (e,p) throws: ein Underflow Error, falls die Prioritätsschlange leer ist
ADT Prioritätsschlange - Interface top(pq, e, p) descr: liefert das Element mit der höchsten Priorität aus der Prioritätsschlange zusammen mit seiner Priorität. Die Prioritätsschlange wird nicht geändert. pre: pq PQ post: e TElem, p TPriority, e ist das Element mit der höchsten Priorität aus pq und p ist seine Priorität throws: ein Underflow Error, falls die Prioritätsschlange leer ist isempty(pq) descr: überprüft ob die Prioritätsschlange leer ist pre: pq PQ wahr, falls pq keine Elemente enthält post: isempty ቊ falsch, ansonsten
ADT Prioritätsschlange - Interface isfull(pq) descr: überprüft ob die Schlange voll ist pre: pq PQ wahr, falls pq voll ist post: isfull ቊ falsch, ansonsten Bemerkung! Die Prioritätsschlange kann nicht iteriert werden! Dafür gibt es auch keine iterator Operation.
ADT Prioritätsschlange - Repräsentierung Um ADT Prioritätsschlange zu implementieren kann man folgende Datenstrukturen für die Repräsentierung benutzen: Dynamisches Array Verkettete Liste Heap
ADT Prioritätsschlange - Repräsentierung Falls die Repräsentierung ein Dynamisches Array oder eine Verkettete Liste ist, dann muss man entscheiden wie die Elemente gespeichert werden: Man kann die Elemente sortiert nach der Priorität speichern Man kann die Elemente in der Reihenfolge, in der sie eingefügt wurden, speichern
ADT Prioritätsschlange - Repräsentierung Komplexität der Operationen für die zwei Repräsentierungs- Möglichkeiten: Operation Sortiert Nicht-sortiert push O(n) Θ(1) pop Θ(1) Θ(n) top Θ(1) Θ(n) Was passiert wenn man in einem separaten Feld das Element mit der höchsten Priorität speichert?
ADT Prioritätsschlange Repräsentierung auf binären Heap Eine andere mögliche Repräsentierung der Prioritätsschlange ist mit Hilfe eines binären Heaps, in welchem das Element mit der höchsten Priorität in der Wurzel des Heaps gespeichert wird
ADT Prioritätsschlange Repräsentierung auf binären Heap Wenn ein Element in die Prioritätsschlange eingefügt wird, dann braucht man nur das Element in den Heap einzufügen (mit bubble-up falls nötig) Wenn ein Element aus der Prioritätsschlange entfernt wird, dann wird die Wurzel des Heaps entfernt (mit bubble-down falls nötig) Bei top wird die Wurzel zurückgeben
ADT Prioritätsschlange Repräsentierung Komplexitäten für die unterschiedlichen Repräsentierungen: Operation Sortiert Nicht-sortiert Heap push O(n) Θ(1) O(log 2 n) pop Θ(1) Θ(n) O(log 2 n) top Θ(1) Θ(n) Θ(1) Welche ist die totale Komplexität für folgende Sequenz von Operationen: Man fängt mit einer leeren Prioritätsschlange an Man fügt n Elemente in die Prioritätsschlange Man entfernt n Elemente aus der Prioritätsschlange
Prioritätsschlange Anwendungen Aufgaben, die mit Hilfe einer Prioritätsschlange gelöst werden können: Stipendienvergabe (siehe Seminar 5) Triage Prozedur in Krankenhäusern
Prioritätsschlange Erweiterung Wir haben die Standard-Operationen aus dem Interface der Prioritätsschlange besprochen: push pop top isempty init Manchmal sind folgende Operationen auch nützlich: Die Priorität eines Elementes erhöhen Ein beliebiges Element löschen Merge zweier Prioritätsschlangen
Prioritätsschlange Erweiterung Welche ist die Komplexität dieser Operationen wenn die Prioritätsschlange auf binären Heap repräsentiert ist: Die Priorität eines Elementes erhöhen ist O(log 2 n) wenn man die Position des Elementes kennt Ein beliebiges Element löschen ist O(log 2 n) wenn man die Position des Elementes kennt Merge zweier Prioritätsschlangen hat die Komplexität Θ(n) (man nimmt an, dass beide Prioritätsschlangen n Elemente enthalten
Prioritätsschlange andere Repräsentierungen Wenn man kein Merge zwischen Prioritätsschlangen braucht, dann sind binären Heaps gute Repräsentierungen Wenn man die Merge Operation braucht, dann gibt es andere Datenstrukturen, die als Repräsentierung für Prioritätsschlangen besser geeignet sind (kleinere Komplexität) Davon besprechen wir: Binomial-Heap
Binomial-Heap Ein Binomial-Heap besteht aus einer Sequenz von Binomial-Bäumen verschiedener Ordnung. Binomial-Bäume können wie folgt rekursiv definiert werden: Ein Binomial-Baum der Ordnung 0 besteht aus einem einzelnen Knoten. Ein Binomial-Baum der Ordnung k besitzt eine Wurzel mit Grad k, deren Kinder genau die Ordnung k-1, k-2,..., 0 (in dieser Reihenfolge) besitzen.
Binomial-Baum Beispiel Binomal Bäume der Ordnung 0, 1, 2 und 3
Binomial-Baum Ein Binomial-Baum der Ordnung k hat genau 2 k Knoten Die Höhe eines Binomial-Baumes der Ordnung k ist k Wenn man die Wurzel eines Binomial-Baumes der Ordnung k löscht, dann kriegt man k Binomial-Bäume der Ordnung k-1, k-2,..., 2, 1, 0. Ein Binomial-Baum der Ordnung k lässt sich leicht aus zwei Binomial- Bäumen der Ordnung k-1 erstellen (merge), indem einer der beiden Bäume zum am weitesten links stehenden Kind des anderen gemacht wird.
Binomial-Baum Merge Zwei Binomial-Bäume der Ordnung 2 (vor dem Merge)
Binomial-Baum Merge Merge in einem Binomial-Baum der Ordnung 3 eine Möglichkeit
Binomial-Baum Merge Merge in einem Binomial-Baum der Ordnung 3 zweite Möglichkeit
Binomial-Baum Repräsentierung Um einen Binomial-Baum zu implementieren kann man folgende Repräsentierung benutzen: Man braucht eine Struktur für die Knoten. Für jeden Knoten speichert man Folgendes: Die Information aus dem Knoten Die Adresse des Vaterknotens Die Adresse des ersten Kind-Knotens Die Adresse des nächsten Bruder-Knotens Für den Baum speichert man die Adresse des Wurzelknotens (und vielleicht die Ordnung des Baumes)
Binomial-Heap Ein Binomial-Heap besteht aus einer Sequenz von Binomial-Bäumen mit folgenden Eigenschaften: Jeder Binomial-Baum erfüllt die Heap-Eigenschaft: für jeden Knoten ist der Wert kleiner als die Werte seiner Kinder Es gibt höchstens ein Binomial-Baum der Ordnung k Als Repräsentierung, ist ein Binomial-Heap meistens eine sortierte verkettete Liste, wobei jeder Knoten ein Binomial-Baum enthält und die Knoten nach der Ordnung der Bäume sortiert sind
Binomial-Heap Beispiel Binomial-Heap mit 14 Knoten; besteht aus 3 Binomial-Bäume der Ordnung 1, 2 und 3
Binomial-Heap Für eine gegebene Anzahl von Elementen n, ist die Struktur des Binomial- Heaps (die Anzahl der Binomial-Bäume und deren Ordnung) eindeutig Die Struktur der Binomial-Heap wird von der binären Darstellung der Zahl n bestimmt Zum Beispiel 14 = 1110 2 = 2 3 + 2 2 + 2 1, d.h. ein Binomial-Heap mit 14 Knoten enthält Binomial-Bäume der Ordnung 3, 2 und 1 (sortiert in umgekehrter Reihenfolge 1, 2,3) Zum Beispiel 21 = 10101 = 2 4 + 2 2 + 2 0, d.h. ein Binomial-Heap mit 21 Knoten enthält Binomial-Bäume der Ordnung 4, 2 und 0
Binomial-Heap Ein Binomial-Heap mit n Elementen enthält höchstens log 2 n Binomial- Bäume Die Höhe des Binomial-Heaps ist höchstens log 2 n
Binomial-Heap Merge Die interessante Operation des Binomial-Heaps ist die Merge Operation, die auch von anderen Operationen benutzt wird Da beide Binomial-Heaps sortierte verkettete Listen sind, besteht der erste Schritt der Merge-Operation aus dem Merge der zwei sortierten Listen Das Ergebnis des Merges kann zwei Binomial-Bäume derselben Ordnung enthalten. Man muss also die Liste iterieren und Binomial-Bäume derselben Ordnung k werden in einem Binomial-Baum der Ordnung k+1 vereinigt. Bei dem Merge der Binomial-Bäume muss die Heap-Eigenschaft aufbewahrt werden
Binomial-Heap Merge Beispiel
Binomial-Heap Merge Beispiel Nach dem Merge der zwei verketteten Listen von :
Binomial-Heap Merge Beispiel Nach dem Merge der Binomial-Bäume derselben Ordnung:
Binomial-Heap Merge Operation Wenn beide Binomial-Heaps n Elemente enthalten, dann ist die Komplexität des Merges O(log 2 n) (die maximale Anzahl der Binomial- Bäume aus einer Binomial-Heap mit n Elementen ist log 2 n)
Binomial-Heap andere Operationen Fast alle anderen Operationen für Binomial-Heap benutzen die Merge Operation Push Operation: Um ein neues Element einzufügen erstellt man einen Binomial-Heap, der dieses Element enthält, und man berechnet den Merge mit dem ursprünglichen Binomial-Heap. Komplexität: O (log 2 n) im schlimmsten Fall (Θ 1 amortisiert) Top Operation: Das kleinste Element aus der Binomial-Heap (das Element mit der höchsten Priorität) ist die Wurzel einer der Binomial-Bäumen. Um das Minimum zurückzugeben muss man über die Wurzeln iterieren, also die Komplexität ist O (log 2 n)
Binomial-Heap andere Operationen Pop Operation: Das Minimum entfernen heißt die Wurzel einer der Binomial-Bäumen zu entfernen. Wenn man die Wurzel aus einem Binomial-Baum löscht, dann erhält man eine Sequenz von Binomial-Bäumen. Diese Bäume werden als Binomial-Heap betrachtet (in umgekehrter Reihenfolge). Man berechnet den Merge zwischen den neuen Binomial-Heap und den Rest der Elementen aus dem ursprünglichen Binomial-Heap Die Komplexität ist O (log 2 n)
Binomial-Heap pop Das Minimum ist in einer der Wurzeln
Binomial-Heap pop Aus dem entsprechenden Binomial-Baum bleiben k Binomial-Bäume
Binomial-Heap pop Betrachte diese als ein Binomial-Heap
Binomial-Heap pop Berechne den Merge zwischen den zwei Binomial-Heaps Erster Schritt der Merge-Operation:
Binomial-Heap pop Zweiter Schritt der Merge-Operation:
Binomial-Heap andere Operationen Priorität eines Elementes erhöhen: Wenn man ein Zeiger zu dem Element hat, deren Priorität erhöht werden muss, dann kann man die Priorität ändern und dann bubble-up ausführen falls die Priorität größer ist als die Priorität des Vaters (in den Beispielen hohe Priorität heißt kleine Zahl) Komplexität: O (log 2 n) Ein beliebiges Element löschen: Wenn man ein Zeiger zu dem Element hat, das man löschen will, dann kann man seine Priorität auf - setzen (d.h. das Element wird bis zu dem Wurzel verschoben) und dann löscht man ein Element (also die Wurzel) Komplexität: O (log 2 n)
Denk darüber nach Verkettete Listen: Man nimmt an, dass es zwei einfach verkettete Listen gibt, die sich in einem Punkt treffen und von da an gemeinsame Knoten haben. Die Anzahl der Knoten aus der zwei Listen vor dem gemeinsamen Teil ist nicht bekannt. Schreibe einen Algorithmus, der den ersten gemeinsamen Knoten findet (Tipp benutze ein Stack)
Denk darüber nach Stack und Schlange Wie kann man ein Stack mit Hilfe von zwei Schlangen implementieren? Welche sind die Komplexitäten der Operationen? Wie kann man eine Schlange mit Hilfe von zwei Stacks implementieren? Welche sind die Komplexitäten der Operationen? Wie kann man zwei Stacks auf einem Array implementieren?
Denk darüber nach Stack und Schlange Gegeben sei eine Sequenz von Charaktern. Schreibe einen rekursiven Algorithmus, der benachbarte Duplikate entfernt. Zum Beispiel, für die Sequenz mississippi ist das Ergebnis m Gegeben sei eine Zahl k und eine Schlange. Wie kann man die ersten k Elemente der Schlange in umgekehrter Reihenfolge speichern (reverse) z.b. k=4, Schlange: [10, 20, 30, 40, 50, 60, 70, 80, 90], das Ergebnis ist: [40, 30, 20, 10, 50, 60, 70, 80, 90].
Denk darüber nach Prioritätsschlange: Wie kann man ein Stack mit Hilfe einer Prioritätsschlange implementieren? Wie kann man eine Schlange mit Hilfe einer Prioritätsschlange implementieren?