DS Vorlesung - TU Chemnitz

Werbung
Vorlesung Datenstrukturen
Heaps
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 469
Prioritätswarteschlange
Problem
Häufig ist das Prinzip einer einfachen Warteschlangen-Datenstruktur nicht ausreichend.
Beispiele
Mautstellen, Bevorzugung behinderter Personen, Prozessmanagement in Betriebssystemen,
Greedy-Algorithmen, ...
Abhilfe
Verwendung einer modifizierten Variante der Schlangendatenstruktur, einer sogenannten
Prioritätswarteschlange (engl. priority queue).
Prioritätswarteschlange
Elemente werden in Abhängigkeit von ihrer Priorität und ihrer Position aus der
Warteschlange entnommen.
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 470
Realisierung
Intuitive Realisierung
Prioritätswarteschlangen können ebenfalls mit Hilfe von Listen implementiert werden. Wir unterscheiden hierbei zwei Varianten:
1. Verwendung einer Schlangen-Datenstruktur mit Einfügen neuer Elemente am Ende der Liste.
2. Geordnetes Einfügen neuer Elemente in die Liste gemäß ihrer Priorität.
Nachteil der ersten Variante
Die Entnahme (dequeue) des Elements mit der höchsten Priorität läuft in O(n), da dieses
Element erst gesucht werden muss.
Nachteil von Variante 2
Das Einfügen (enqueue) eines Elements gemäß seiner Priorität läuft in O(n), da die
Einfügeposition nur durch sequentielles Durchlaufen der Liste bestimmt werden kann.
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 471
Optimierung
Konsequenz
Beide Varianten haben totale Operationskosten von O(n)
Verbesserungsvorschläge
Es gibt einige Verbesserungsvorschläge dieser naiven Listenimplementierung. So ergänzt z.B.
Hendriksen (1977) die herkömmliche Listenimplementierung um ein Feld von Zeigern (Prinzip
Indexierung), um schneller den Elementbereich in der Liste zu bestimmen, in den ein neues
Element einzufügen ist.
Ergebnis
Die Operationskosten der verbesserten Listenimplementierungen sinken im Schnitt auf O(√n).
Frage
Lässt sich das Laufzeitverhalten noch weiter verbessern?
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 472
Heaps
Definition
Ein Heap ist eine besondere Form eines binären Baums und es gilt, dass
• der Wert eines Knotens in einer Ordnungsrelation zu den Werten seiner Kinder steht
• er perfekt balanciert ist und die Blätter der letzten Ebene linksbündig vollständig sind
Ordnungsrelation
In Abhängigkeit der Beziehung zwischen einem Knoten und seinen Kindern unterscheiden
wir zwei Heapvarianten:
• MinHeap - Der Wert eines Knotens ist kleiner oder gleich den Werten seiner Kinder.
• MaxHeap - Der Wert eines Knotens ist größer oder gleich den Werten seiner Kinder.
Achtung
Es wird keine Aussage über eine direkte Beziehung zwischen den Kindknoten getroffen!
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 473
Implementierung von Heaps
Konsequenz der Heap-Definition
Alle Blätter befinden sich auf den letzten beiden Ebenen und bis auf die letzte Ebene sind
alle Ebenen vollständig gefüllt, d.h. jede innere Ebene enthält doppelt so viele Elemente wie
die Vorgängerebene. Des Weiteren befinden sich keine „Lücken“ zwischen den Blättern der
letzten Ebene. Auf diesem Grund können Heaps mit Hilfe von Feldern dargestellt werden.
Darstellung als Feld
Die Eltern-Kind-Beziehung eines MaxHeaps in einem Feld maxHeap der Länge N kann wie
folgt definiert werden:
maxHeap[i] ≥ maxHeap[2*i+1] für 0 ≤ i < (N–1) / 2
(Relation zu linkem Kind)
maxHeap[i] ≥ maxHeap[2*i+2] für 0 ≤ i < (N–2) / 2
(Relation zu rechtem Kind)
Transformation in Felddarstellung
Die Level-Order-Traversierung eines als Binärbaum realisierten Heaps liefert die korrekte
Reihenfolge der Feldelemente.
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 474
Operationen auf Heaps
Anwendungsbereiche
Heaps sind prädestiniert für Anwendungsgebiete, bei denen schnell entweder das kleinste
(MinHeap) oder das größte Element (MaxHeap) einer Datenmenge bestimmt werden muss.
Heaps sind aufgrund der fehlenden Relation zwischen Kindknoten nicht geeignet für die
Suche nach einem beliebigen Element.
Konsequenz für Operationen
Wir beschränken uns aufgrund der Anwendungsbereiche neben dem Lesen des
Wurzelelements auf das Einfügen eines neuen Elements in einen Heap (heapEnqueue) und
das Entfernen des Wurzelelements (heapDequeue).
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 475
heapEnqueue
1. Einfügen eines Elements
Um ein Element el einem Heap hinzuzufügen, fügen wir el (am Ende des Feldes)
als letztes Blatt hinzu.
2. Rekonstruktion der Heap-Eigenschaft
Nach dem Einfügen von el ist in der Regel die Heap-Eigenschaft verletzt.
Diese muss wiederhergestellt werden, indem el gemäß der Ordnungsrelation
solange mit seinem jeweiligen Vaterknoten vertauscht wird, bis sich el an der
richtigen Position im Heap befindet.
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 476
heapDequeue
Prinzip
Wir entfernen das Wurzelelement aus dem Heap und ersetzen es durch den letzten Blattknoten.
Auch hier muss die Heap-Eigenschaft wiederhergestellt werden. Dies geschieht, indem der neue
Wurzelknoten solange abwärts in Richtung Blattebene verschoben wird, bis sich der Knoten
gemäß der Ordnungsrelation an der richtigen Position im Heap befindet. Allerdings stehen jetzt
zwei Kindknoten zur Auswahl:
Auswahlstrategie bei MinHeap
Ist der abwärts zu verschiebende Knoten größer als beide Kindknoten, dann vertausche ihn mit
dem kleineren Kind (das danach als Elternknoten des größeren Kindes fungiert).
Auswahlstrategie bei MaxHeap
Ist der abwärts zu verschiebende Knoten kleiner als beide Kindknoten, dann vertausche ihn mit
dem größeren Kind (das danach als Elternknoten des kleineren Kindes fungiert).
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 477
Abwärtsbewegung bei MaxHeap
void moveDown(Elementtyp h[], int parent, int N) {
// parent = (Teilbaum)-Wurzelindex, N = Elementanzahl des Feldes h
int largest = 2 * parent + 1; // Kindknoten initialisieren
while (largest < N ) {
if ( largest < N-1 && h[largest] < h[largest+1] )
largest++;
// zweites Kind ist größer als erster Kindknoten
if ( h[parent] < h[largest] ) {
// Heapeigenschaft verletzt?
Elementtyp temp = h[parent]; // Knoten tauschen
h[parent] = h[largest];
h[largest] = temp;
parent = largest;
largest = 2 * parent + 1;
}
else largest = N;
// Schleifenabbruch wenn Heap korrekt
}
}
[nach Drozdek]
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 478
Anordnung eines Feldes als Heap (1)
Verfahren nach Williams (Top-Down)
1. Wir schaffen Platz für ein neues Feld mit genauso viel Elementen wie das zu
konvertierende Feld. Dieses neue Feld repräsentiert zunächst einen leeren Heap.
2. Wir fügen nacheinander alle Elemente des Ausgangsfeldes mittels heapEnqueue in den
Heap (das neue Feld) ein
Komplexität
Die Laufzeit des Algorithmus für n Elemente beträgt im Worst Case O(n log n).
Allerdings haben Untersuchungen gezeigt (Hayward und McDiarmid 1991), dass der
Algorithmus im Average Case in Linearzeit, also in O(n) läuft.
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 479
Anordnung eines Feldes als Heap (2)
Verfahren nach Floyd (Bottom-Up)
Das Grundprinzip besteht in der Bildung kleiner Heaps, die nach und nach zu größeren Heaps
verschmolzen werden.
Ausgangspunkt
Für alle Blätter ist die Heapeigenschaft erfüllt.
Algorithmus:
Beginnend mit dem letzten Nichtblattknoten wird jeder Nichtblattknoten, der die Heapeigenschaft
verletzt, solange in Richtung der Blattknoten verschoben bis die Heapeigenschaft wieder erfüllt ist.
Komplexität
Die Komplexität dieses Algorithmus beträgt grundsätzlich, d.h. selbst im worst case O(n).
Im Average Case sind jedoch Top-Down- und Bottom-Up-Verfahren vergleichbar.
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 480
Verwendung von Heaps (1)
Heaps als Prioritätswarteschlangen
Mit Heaps lassen sich Prioritätswarteschlangen effizient realisieren, da sich das Element mit dem
höchsten Wert (der höchsten Priorität) automatisch in der Wurzel eines MaxHeaps befinden würde.
Komplexität
Da Heaps perfekt balanciert sind, lässt sich ein Blatt von der Wurzel aus in O(log n) Schritten erreichen.
Entnehmen der Wurzel aus Heap (= dequeue der Prioritätswarteschlange) benötigt maximal O(log n)
Schritte um die Heapeigenschaft zu rekonstruieren.
Einfügen eines Elements in den Heap (= enqueue der Prioritätswarteschlange) benötigt maximal O(log n) Schritte um die Heapeigenschaft zu rekonstruieren.
Konsequenz
Die zwei wesentlichen Operationen der Prioritätswarteschlange laufen in O(log n) und sind damit
wesentlich effizienter als die Listenimplementierung mit O(n) und auch effizienter als die optimierten
Varianten mit O(√n).
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 481
Verwendung von Heaps (2)
Sortieren
Wir entnehmen via heapDequeue solange das Minimum bzw. Maximum aus einem Heap und
fügen dieses (ggf. umgekehrt) sequentiell in ein Feld ein, bis der Heap leer ist.
Heapsort für MaxHeap
1. Generiere einen Heap aus einem Feld h mit N Elementen
2. for (int i = N-1; i >= 1; i--) {
// Tausche das Maximum des Heaps mit h[i]
// Stelle Heapeigenschaft für den Heap mit i Elementen (Feld h von 0 bis i–1) wieder her
}
Komplexität
• Generieren des Heaps aus Array ➔ O(n), n-maliger Tausch des Maximums O(n)
• Wiederherstellung der Heapeigenschaft ➔ O(n log n)
• Gesamtkomplexität O(n log n)
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 482
Heapsort
void heapSort(Elementtyp h[], int N) {// h = Feld mit N Elementen
for (int i = N/2-1; i >= 0; i--)
// Erzeugen des Heaps
moveDown(h, i, N);
for (int i = N-1; i >= 1; i--) {
Elementtyp temp = h[i];
// Maximum aus Heap mit letztem
h[i] = h[0];
// Feldelement tauschen
h[0] = temp;
moveDown(h, 0, i);
}
// Heapwiederherstellung für
// nächstkleineren Heap
}
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 483
Zusammenfassung
Heaps sind geeignet
• für das schnelle Auffinden in O(1) des Minimums (MinHeap) oder Maximums (MaxHeap)
• für das schnelle Entnehmen der Wurzel und das schnelle Einfügen eines neuen Knotens in
den Heap, da beide Operationen in O(log n) laufen
Heaps sind nicht geeignet
• für das Auffinden beliebiger Elemente im Baum, da Komplexität O(n)
Anwendung
Heaps sind ideal für eine effiziente Realisierung von Prioritätswarteschlangen (z.B. für DijkstraAlgorithmus) und u.a. als Hilfsmittel für Greedy-Algorithmen und das Heapsort-Verfahren.
Besonderheit
Heaps können mit Feldern realisiert werden ➔ Aber potenzielles Problem mit starrer Feldgröße
bei dynamischen Operationen wie Einfügen und Löschen.
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 484
Ende der Vorlesung
Dr. Frank Seifert
Vorlesung Datenstrukturen - Sommersemester 2016
Folie 485
Herunterladen