Was bisher geschah ADT Menge mit Operationen: I Suche nach einem Element I Einfügen eines Elementes I Löschen eines Elementes Realisierung durch verschiedene Datenstrukturen: I lineare Datenstrukturen: Array, Liste, Hashtabelle I hierarchische Datenstrukturen: Suchbäume, balancierte Suchbäume Datenstrukturen mit Zugriff nur auf ein spezielles Element: Stack, Queue mit 1. Ansehen (top, first) 2. Entfernen (pop, dequeue) 3. Einfügen einen Elementes (push, enqueue) 172 Priority-Queues (Vorrang-Warteschlange) Idee: I Modellierung von Prioritäten, z.B. Dringlichkeit I Datenstruktur zur Verwaltung von Paaren (Schlüssel, Element) I Schlüssel repräsentieren Priorität I oft Rangfolge: kleiner Schlüssel = hohe Priorität I Zugriff nur auf ein Element mit minimalem Schlüssel (maximale Priorität) 173 Beispiele I Notfallaufnahme Prioritäten: lebensbedrohliche (1), schwere (2), leichte (3) Verletzungen I Betriebssysteme: Scheduling von Prozessen I kürzeste Wege in Graphen (Dijkstra) I Expansionsreihenfolge der Knoten in Spielbäumen 174 ADT Priority-Queue Menge (E, ≤) von Schlüsseln (Prioritäten) mit einer totalen Ordnung Sorten: Bool, E, PQhEi Signatur: EmptyPQ : isEmpty : PQhEi → add : PQhEi × E → getmin : PQhEi → deletemin : PQhEi → PQhEi Bool PQhEi E PQhEi Axiome (Ausschnitt) jeweils ∀p, q ∈ PQhEi ∀e, f ∈ E : isEmpty(emptyPQ) = t, isEmpty(add(p, e)) = f, (isEmpty(p) ∨ e < getmin(p)) ↔ getmin(add(p, e)) = e, (isEmpty(p) ∨ e < getmin(p)) ↔ deletemin(add(p, e)) = p, Φ= e > getmin(p) → getmin(add(p, e)) = getmin(p), e > getmin(p) → deletemin(add(p, e)) = add(deletemin(p), e) (+ Axiome der Booleschen Algebra) nur für Spezifikationen: Inhalt(p) = Menge aller Schlüssel in p 175 Wiederholung: Stack, Queue Datenstruktur (DS) mit get / remove nur für spezielles Element: Sorten: Bool, E, DShEi Signatur: EmptyDS : isEmpty : DShEi → add : DShEi × E → get0 : DShEi → DShEi → remove0 : DShEi Bool DShEi E DShEi Spezialfälle: Stack mit anderen Namen für Operationen: add 7→ push, remove’ 7→ pop, get’ 7→ top spezielles = „neuestes“ enthaltenes Element Queue mit anderen Namen für Operationen: add 7→ enqueue, remove’ 7→ dequeue, get’ 7→ front spezielles = „ältestes“ enthaltenes Element Priority-Queue mit anderen Namen für Operationen: add 7→ add, remove’ 7→ deletemin, get’ 7→ getmin spezielles = minimales enthaltenes Element 176 Stack und Queue als Priority-Queues Queue Priorität jedes neu in die PQ eingefügten Elementes kleiner (Schlüssel größer) als Maximum aller schon in der PQ enthaltenen Elemente (Einfügen aufsteigend geordneter Schlüsselfolge) Stack Priorität jedes neu in die PQ eingefügten Elementes größer (Schlüssel kleiner) als Maximum aller schon in der PQ enthaltenen Elemente (Einfügen absteigend geordneter Schlüsselfolge) 177 Sortieren mit Priority-Queue Wiederholung: Spezifikation des Sortier-Problemes V: Eingabe (x1 , x2 , . . . , xn ) mit ∀i ∈ {1, . . . , n} : xi ∈ N N: Ausgabe (y1 , y2 , . . . , yn ) ist 1. y1 < y2 ≤ · · · < yn+1 (aufsteigend geordnet) und 2. eine Permutation (Umordnung) der Eingabe (x1 , x2 , . . . , xn ) Algorithmus : PQ-Sort Eingabe : x = (x1 , . . . , xn ) Ausgabe : y = (y1 , . . . , yn ) p ← emptyPQ für jedes i ← 1, . . . , n : p ← add(p, xi ) für jedes i ← 1, . . . , n : yi ← getmin(p) p ← deletemin(p) Laufzeit: n-mal add + n-mal (getmin + deletemin) abhängig von Realisierung der PQ 178 PQ – Realisierung durch Folgen unsortierte Folge: I add: (als erstes / letztes Element) O(1) bis O(n) (Liste, Array) I getmin, deletemin: (Minimum-Suche) O(n) I PQ-Sortierverfahren: O(n2 ) sortierte Folge: I add: (aufsteigend) sortiertes Einfügen in O(n) I getmin: (erstes / letztes Element) O(1) I deletemin: (erstes / letztes Element) O(1) bis O(n) (Liste, Array) I PQ-Sortierverfahren: O(n2 ) 179 PQ – Realisierung durch Bäume Binäre Suchbäume: Laufzeiten: Tiefe des Baumes I add: sortiertes Einfügen in O(log n) bis O(n) I getmin, deletemin: (linker äußerer Knoten) O(log n) bis O(n) I PQ-Sortierverfahren: O(n log n) bis O(n2 ) Suchbäume mit geeigneter Balance-Eigenschaft (z.B. AVL-Bäume, B-Bäume, Rot-Schwarz-Bäume): Laufzeiten: Tiefe des Baumes I add: sortiertes Einfügen in O(log n) I getmin, deletemin: O(log n) I PQ-Sortierverfahren: O(n log n) 180 Binäre Heaps Ein Binärbaum t erfüllt die Heap-Eigenschaft gdw. t erfüllt beide folgende Eigenschaften Struktureigenschaft (Balance): t hat Heap-Form, d.h. 1. vollständig balancierter Binärbaum der Tiefe blog(size(t))c und restliche Knoten in 2. Blatt-Schicht „von links vollständig“ belegt Ordnungseigenschaft: t ist Heap-geordnet, d.h. in jedem Knoten Branch(x, Branch(y , l, r ), Branch(z, s, t)) in t gilt: x ≤ y und x ≤ z Auf jedem Pfad von der Wurzel zu einem Blatt eines Heap-geordneten Baumes sind die Schlüssel aufsteigend geordnet. 181 Binärer Heap – add Spezifikation add V: Eingabe binärer Heap t, Schlüssel k N: Ausgabe binärer Heap s mit Inhalt(s) = Inhalt(t) ∪ {k } Idee zum Einfügen eines Schlüssels k in t (in zwei Schritten): 1. Neuen Knoten mit Schlüssel k auf der ersten freien Position im Heap t (Blattebene) einfügen (ggf. neue Blattebene beginnen) Heap-Ordnung ist danach evtl. verletzt 2. rekursiv entlang des Pfades vom neu eingefügten Knotens zur Wurzel: solange Schlüssel des Kindes größer als Schlüssel des Vaters: Schlüssel austauschen (bubble) Warum erhält diese Operation die Heap-Eigenschaft? Laufzeit: O(log n) (Tiefe des Baumes) 182 Binärer Heap – getmin Spezifikation getmin V: Eingabe (nichtleerer) binären Heap t N: Ausgabe Schlüssel m = min(Inhalt(t)) (d.h. ∀k 0 ∈ Inhalt(t) : m < k 0 ) Idee: Rückgabe des Schlüssels in der Wurzel des Heap getmin(Branch(k , l, r )) ← k Laufzeit: O(1) 183 Binärer Heap – deletemin Spezifikation deletemin V: Eingabe (nichtleerer) Heap t N: Ausgabe binärer Heap s mit Inhalt(s) = Inhalt(t) \ {min(Inhalt(t))} Idee: Löschen des Wurzelknotens im Heap t in zwei Schritten: 1. Kopie des Schlüssels des „letzen“ Blattes in die Wurzel, Löschen des letzten Blattes Heap-Eigenschaft ist danach evtl. verletzt 2. rekursiv beginnend in der Wurzel, solange Schlüssel des Vaters kleiner als Schlüssel eines Kindes: Austausch der Schlüssel von Vater und dem Kind mit dem kleinstem Schlüssel Warum erhält diese Operation die Heap-Eigenschaft? Laufzeit: O(log n) (Tiefe des Baumes) 184 Heapsort (PQ-Sort mit PQ-Realisierung als binärer Heap) Spezifikation Sortieren (aufsteigend): V: Eingabe (x1 , x2 , . . . , xn ) mit ∀i ∈ {1, . . . , n} : xi ∈ N N: Ausgabe (y1 , y2 , . . . , yn ) ist 1. y1 ≤ y2 ≤ · · · ≤ yn+1 (aufsteigend geordnet) und 2. eine Permutation (Umordnung) der Eingabe (x1 , x2 , . . . , xn ) Algorithmus : Heapsort Eingabe : x = (x1 , . . . , xn ) Ausgabe : y = (y1 , . . . , yn ) p ← Leaf (leerer binärer Heap) für jedes i ← 1, . . . , n : p ← add(p, xi ) für jedes i ← 1, . . . , n : yi ← getmin(p) p ← deletemin(p) Gesamtlaufzeit: O(n log n) 185 Repräsentation binärer Heaps als Arrays Idee: Speichern der Knoten des Heap t (Binärbaum) in Array (x1 , . . . , xsize(t) ), wobei I I x1 = Wurzel Nachbarn jedes Knotens xi in x I I Kinder: x2i , x2i+1 Vater: xbi/2c (Zugriff in O(1)) (Zugriff in O(1)) Heapsort-Implementierung häufig mit Array-Repräsentation (in-situ-Sortierverfahren mit Laufzeit O(n log n)) 186