Algorithmen und Datenstrukturen

Werbung
Sortieren
Algorithmen und Datenstrukturen
10. Vorlesung
a1
a2
a3
an
10
1
4
5
1
9
4
1
8
US
SO
RA
OR
RT
HM
LG
IE
IT
Martin Dietzfelbinger
Item:
Schlüssel
16. Juni 2008
Daten:
1
1
1
4
4
5
8
9
10
SO
RT
IE
RA
LG
OR
IT
HM
US
x
"key"
dat
"data"
"Stabilität"
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
Maßzahlen für Sortieralgorithmen: In Abhängigkeit von n
fragen wir nach:
Sortieren
Sortierproblem, abstrakt: Siehe Vorlesung 1.
1. Laufzeit (in O-Notation);
Sortiermethoden, schon gesehen:
2. Anzahl der Vergleiche
Straight Insertion Sort (Vorlesung), Bubble Sort (Übung),
Mergesort (AuP, 1. Semester)
Bei Vorkommen gleicher Sortierschlüssel:
Sortierverfahren heißt stabil, wenn Objekte mit identischen
Schlüsseln in der Ausgabe in derselben Reihenfolge stehen wie
in der Eingabe.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
1
2
A[i] < A[j]
bzw. A[i] ≤ A[j]
( wesentliche Vergleiche“);
”
3. Anzahl der Datenverschiebungen oder Kopiervorgänge
(wichtig bei sehr umfangreichen Objekten);
4. der neben A benötigte Speicherplatz;
wenn dieser nur O(1), also von n unabhängig, ist:
Algorithmus arbeitet in situ“.
”
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
3
Straight Insertion Sort: Θ(n2) Vergleiche, Zeit Θ(n2).
(Schlechtester/durchschnittlicher Fall).
Quicksort - Schnelles Sortieren im
Durchschnitt
(Bester Fall: Θ(n).)
Divide-and-Conquer-Paradigma
Stabil, in situ.
Zerlege – Löse Teilprobleme rekursiv – Kombiniere
Mergesort: Θ(n log n) Vergleiche, Zeit Θ(n log n).
Zentrale Teilaufgabe:
Stabil, nicht in situ (benötigt zusätzlichen Platz Θ(n)).
Sortiere (Teil-)Array A[a..b], wo 1 ≤ a ≤ b ≤ n.
Mergesort mit linearen Listen benötigt keinen Zusatzplatz (die
Datenstruktur beinhaltet jedoch Zeiger).
Zu erledigen von einer (rekursiven) Prozedur r qsort(a, b).
Mergesort ist Basis für externes Sortieren (Daten auf
Hintergrundspeicher).
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
4
AuD – 16.06.2008
Quicksort−Schema:
0) Trivialitätstest: Wenn a ≥ b, ist nichts zu tun.
Teilaufgabe: Sortiere A[a...b]
(Oder: Wenn b ≤ a + Δ: StraightInsertionSort(a, b).)
1) Teile: (Hier partitioniere“)
”
Wähle x ∈ {A[a],...,A[b]}.
Arrangiere die Einträge von A[a..b] so um, dass
x
4
a
23
5
8
17
9
1
8
b
16
Partitioniere!
4
1
9
9
p
Sortiere
rekursiv
1
5
4
AuD – 16.06.2008
5
23
A[a], . . . ,A[p − 1] ≤ A[p] = x < A[p + 1], . . . ,A[b]
16
17
für ein p mit a ≤ p ≤ b.
>9
2) Löse Teilprobleme rekursiv:
r qsort(a, p − 1) (links), r qsort(p + 1, b) (rechts).
Sortiere
rekursiv
8
9
16
17
5
Ansatz für r qsort(a, b), Korrektheit:
Kernstück: Partitionieren und rekursiv sortieren.
FG KTuEA, TU Ilmenau
FG KTuEA, TU Ilmenau
Nach I.V. gilt nun: A[a..p − 1], A[p + 1..b] sortiert.
23
3) Kombiniere“: Es ist nichts zu tun: A[a..b] ist sortiert.
”
6
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
7
Kernstück: Partitionieren.
Kernstück: Partitionieren. x = A[a]
Woher nehmen wir das Pivotelement“ x?
”
Beispiele: x := A[a] (erstes Element des Teilarrays)
Klar: b − a Vergleiche genügen, um Elemente ≤ x nach
”
links“ und Elemente > x nach rechts“ zu schaffen, und x
”
an die Stelle zwischen den Bereichen zu schreiben.
x := A[b] (letztes Element des Teilarrays)
x := A[(a + b)/2] (Mitte des Teilarrays)
Clever Quicksort“
”
x := der Median der drei Einträge A[a], A[b], A[(a+b)/2]
Eine sehr geschickte Methode (vom Erfinder von Quicksort,
Hoare),
kein zusätzlicher Platz, keine unnötige Datenbewegung.
Randomisiertes Quicksort“
”
Wähle einen der b − a + 1 Einträge in A[a..b] zufällig.
Technisch: Wähle ein s mit a ≤ s ≤ b,
vertausche A[a] mit A[s].
Dann stets: Pivotelement x steht in A[a].
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
a
x
x
8
i
>x
?
j
x
b
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
9
. . . bis sich i und j treffen“ und überkreuzen“.
”
”
>x
Ignoriere A[a].
a
x
x
j i
z
>x
a
z
x
j i
x
>x
b
Lasse ab a + 1 einen Zeiger“ (Index) i nach rechts wandern,
”
bis ein Eintrag > x erreicht wird.
Lasse ab b einen Zeiger“ j (Index) nach links wandern, bis
”
ein Eintrag ≤ x erreicht wird.
Falls i < j: Vertausche A[i] und A[j].
a
x
x
i
x
?
j
>x
b
p=j
b
Vertausche A[a] mit A[j].
>x
Position p des Pivotelements jetzt: A[j].
REST
Erhöhe i um 1, erniedrige j um 1. Wiederhole.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
Sorgfalt bei der Implementierung der Idee nötig.
10
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
11
Prozedur partition(a, b, p)
(∗ Partitionierungsprozedur in r qsort(a, b) ∗)
(∗ Voraussetzung: a < b; Pivotelement: A[a] ∗)
(1)
x ← A[a];
(2)
i ← a + 1; j ← b;
(3)
while (i ≤ j) do (∗ noch nicht fertig; Hauptschleife ∗)
(4)
while (i ≤ j) and (A[i] ≤ x) do i++;
(5)
while (j > i) and (A[j] > x) do j--;
(6)
if (i = j) then j--; (∗ Wissen: A[i] < x ∗)
(7)
if (i < j) then
(8)
vertausche A[i] und A[j];
(9)
i++; j--;
(10)
A[a] ← A[j];
(11)
A[j] ← x;
(12)
p ← j; (∗ Rückgabewert ∗)
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
x =20
// 7
x =20
// 7
x =20
// 7
12
7
2
5
20 7
a
x =20
// 7
i
x =20
// 7
16 18 30 12 25
8
9 35 31
b
Vorbereitung
16 18 30 12 25
8
9
j
16 18 30 12 25
i
8
9
j
x =20
// 7
16 18 9
12 25
i
8
j
30
x =20
// 7
16 18 9
12 25
i
8
j
30
FG KTuEA, TU Ilmenau
Vertauschen
AuD – 16.06.2008
13
Lemma (Korrektheit von partition)
16 18 9
16 18 9
16 18 9
12 25
i
8
j
30
12
8
j
25 30
i
12 8
25 30
Ein Aufruf von partition(a, b, p) bewirkt folgendes, wenn 1 ≤
a < b ≤ n gilt:
Vertauschen
Sei x = A[a] (vor Aufruf), p der Inhalt von p (nach Aufruf).
Das Teilarray A[a . . b] wird durch partition(a, b, p) so umarrangiert, dass x in A[p] steht und dass
fertig!
A[a],. . . ,A[p − 1] ≤ x < A[p + 1],. . . ,A[b]
Aufräumen
8
Beispiel für partition:
16 18 9
gilt.
12 20 25 30
p
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
14
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
15
Beweis:
Wir beobachten die folgende Schleifeninvariante für die
Hauptschleife (3)–(9)
für a, b und die Inhalte i, j, x von i, j, x:
Nach dem Test in Zeile (3), vor Beginn von Zeile (4) gilt:
a+1≤i≤j ≤b
A[a + 1], . . . ,A[i − 1] ≤ x
A[j + 1], . . . ,A[b] > x
Für den Beweis stellt man fest:
Nach der i-Schleife in Zeile (4) gilt (!):
a ≤ i − 1 ≤ j,
A[a + 1], . . . ,A[i − 1] ≤ x,
i ≤ j ⇒ A[i] > x.
Nach Zeile (6) gilt:
a ≤ i − 1 ≤ j,
A[j + 1], . . . ,A[b] > x,
i ≤ j ⇒ (i < j und A[j] ≤ x).
Beweis durch Induktion über Schleifendurchläufe.
1. Fall: i > j. Dann gilt i = j + 1 und A[a + 1], . . . ,A[j] ≤ x
und A[i], . . . ,A[b] > x.
Hauptschleife endet, Vertauschung (Zeilen (10), (11)) stellt
verlangte Ausgabe her.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
16
2. Fall: i ≤ j. Dann gilt wegen Zeile (6) sogar i < j.
Durch die Vertauschung und Änderung der Indizes (Zeilen (8),
(9)) entsteht wieder die Schleifeninvariante (falls i ≤ j) oder
das korrekte Ende (i > j) der äußeren Schleife.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
17
Lemma (Aufwand von partition(a, b, p))
(a) Die Anzahl der Schlüsselvergleiche ist exakt b − a;
(b) der (Zeit-)Aufwand ist Θ(b − a).
Beweis: (a) Nach jedem Schlüsselvergleich wird i erhöht (Zeile
(4) oder (9)) oder j erniedrigt (Zeile (5) oder (9)).
In der Situation i = j wird der Vergleich A[i] ≤ x durchgeführt, und dies ist der letzte Vergleich.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
18
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
19
Prozedur r qsort(a, b)
(∗ Rekursive Prozedur im Quicksort-Algorithmus, 1 ≤ a < b ≤ n ∗)
(1)
s ← ein Element von {a, . . . , b};
(∗ Es gibt verschiedene Methoden/Arten, s zu wählen! ∗)
(2)
Vertausche A[a] und A[s];
(3)
partition(a, b, p); (∗ p: Ausgabeparameter ∗)
(4)
if a < p − 1 then r qsort(a, p − 1);
(5)
if p + 1 < b then r qsort(p + 1, b).
Lemma
Ein Aufruf von r qsort(a, b) sortiert A[a . . b].
(Beweis schon gesehen.)
Um A[1 . . n] zu sortieren:
Prozedur quicksort(A[1..n])
if n > 1 then r qsort(1, n)..
(1)
Alternative zu den Tests in Zeilen (4), (5):
Teilarrays der Länge ≤ Δ direkt mit StraightInsertionSort
sortieren, für eine Konstante Δ.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
20
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
21
Die Laufzeit von r qsort(a, b), wobei n = b − a + 1.
Schlechtester Fall: (worst case)
Der Aufwand für einen Aufruf ist O(1)
Wenn zwei Schlüssel verglichen werden, ist einer davon (x)
das Pivotelement, der andere wandert in eines der beiden
Teilarrays.
plus der Aufwand für partition(a, b, p)
plus der Aufwand für die rekursiven Aufrufe.
Der Aufwand für partition(a, b, p) ist proportional zu b − a,
und das ist genau die Anzahl der Schlüsselvergleiche, die
partition(a, b, p) durchführt.
Konsequenz: Es werden niemals dieselben Schlüssel später
nochmals verglichen.
Also: V ≤ n2 = 12 n(n − 1).
In jedem Aufruf ist ein anderer Arrayeintrag Pivotelement.
Dieser Wert kommt vor, wenn die Eingabe schon sortiert ist
und man in Prozedur r qsort(a, b) immer A[a] als Pivotelement wählt.
Gesamtaufwand für r qsort(a, b), wobei n = b − a + 1:
O(n) plus Gesamtanzahl
allerSchlüsselvergleiche.
=:V
V ist eine Größe, die von der Anordnung der Eingabe abhängt.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
22
(Dann ist das linke Teilarray leer, das rechte enthält b − a Elemente.
Rekursionstiefe n, und V = (n − 1) + (n − 2) + · · · + 3 + 2 + 1 = n2 .)
Actung: Ähnlicher Effekt, wenn Array fast“ sortiert.
”
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
23
Cn := die erwartete Anzahl der Vergleiche beim Sortieren
eines Teilarrays mit n Einträgen mit r qsort
Durchschnittlicher/erwarteter Fall?
Wahrscheinlichkeitsannahme:
Die Eingabeelemente sind verschieden.
O.B.d.A.: Eingabeelemente sind n verschiedene Zahlen x1 <
. . . < xn.
Jede Anordnung der
Wahrscheinlichkeit.
Eingabeelemente
hat
dieselbe
(Es gibt n! solche Anordnungen.)
Unter dieser Wahrscheinlichkeitsannahme wird V zu einer
Zufallsgröße.
Wir untersuchen den Erwartungswert/Mittelwert von V .
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
24
Cn: Wenn man beim ersten r qsort(a, b)-Aufruf das i-t
kleinste Element xi als Pivotelement hat,
C0 = 0
C1 = 0
C2 = 1
C3: Wenn man beim ersten r qsort(a, b)-Aufruf mit b = a + 2
das mittlere Element x2 als Pivot hat, gibt es 2 Vergleiche
insgesamt.
Wahrscheinlichkeit hierfür: 13 .
Wenn man x1 oder x3 als Pivot hat, hat man zunächst 2
Vergleiche, und einer der rekursiven Aufrufe führt zu einem
dritten.
Insgesamt: 3 Vergleiche. Wahrscheinlichkeit hierfür: 23 .
Mittlere Vergleichsanzahl: C3 = 13 · 2 + 23 · 3 = 83 .
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
25
Falls also xi Pivotelement ist:
Kosten (n − 1) + Ci−1 + Cn−i.
dann erzeugt dieser Aufruf selbst zunächst genau n − 1
Vergleiche
Die Wahrscheinlichkeit, dass xi Pivotelement ist, ist gleich
1/n, weil
es ergibt sich als partitionierende Position p (in p) genau
a + i − 1,
nach der W-Annahme jede Anordnung der Einträge x1, . . . , xn
in A[a . . b] gleichwahrscheinlich ist.
und es entstehen die rekursiven Aufrufe
Also:
r qsort(a, a + i − 2) (wenn i ≥ 3)
r qsort(a + i, b) (wenn i ≤ n − 2)
n
1 ((n − 1) + Ci−1 + Cn−i).
Cn = ·
n i=1
Diese kosten im Durchschnitt Ci−1 und Cn−i Vergleiche.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
26
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
27
Vorlesung am 19.05.2008, Folie 34:
Satz
Rekursionsformel für die mittlere totale Weglänge A(n) in
zufällig erzeugten binären Suchbäumen.
Die erwartete Vergleichsanzahl bei der Anwendung von Quicksort auf ein Array mit n verschiedenen Einträgen, die zufällig
angeordnet sind, ist
A(0) = A(1) = 0, A(2) = 1,
n
A(n) = (n − 1) + n1 · i=1(A(i − 1) + A(n − i)).
2(n + 1)Hn − 4(n + 1) + 4 ≈ (2 ln 2)n log n − 2,846 . . . n.
Das ist genau die gleiche Rekursion wie die für Cn!
Bemerkung: Kein Zufall, sondern enge Verbindung der beiden
Verfahren Quicksort und Erzeugen von Suchbäumen.
Dabei: 2 ln 2 ≈ 1,386 (die Quicksort-Konstante“)
”
Also sind auch die Lösungen identisch.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
28
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
Bemerkung:
Dieselbe Aussage gilt für die randomisierte Version, bei der
das Pivotelement zufällig gewählt wird,
und zwar für jedes beliebige feste Eingabearray A[1 . . n]
Bemerkungen:
Randomisierung
Quicksort.
• Vermeide rekursive Aufrufe für allzu kleine Teilarrays.
Direkt mit StraightInsertionSort sortieren.
verhindert
worst-case-Eingaben
bei
Bemerkung:
Eine O(n log n)-Laufzeit stellt sich bei (randomisiertem)
Quicksort sogar mit hoher Wahrscheinlichkeit ein.
( Zuverlässigkeitsaussage“.)
”
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
30
29
• Quicksort ist in der Praxis sehr schnell.
(Partition-Prozedur ist Cache-freundlich)
• Noch besser: Rekursion bei Teilarrays der Länge Δ
(z.B. Δ = 10) abbrechen, unsortiert lassen.
Am Ende: ein Aufruf StraightInsertionSort(1, n).
• Warnung 1: Wenn A[1 . . n] teilweise sortiert ist, und man
das Pivotelement naiv wählt, erhält man schlechte Laufzeiten. (Bis zu Ω(n2).)
Abhilfe: Median-of-3 oder Randomisiertes Quicksort.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
31
Bemerkungen:
• Warnung 2: Wenn A[1 . . n] viele identische Schlüssel
enthält, ist das beschriebene Verfahren nicht günstig. (Besondere Vorkehrungen im Programm nötig: Übung.)
• Quicksort ist nicht stabil.
• Platz (für den Rekursionsstack) kann im schlechtesten Fall
bis zu O(n) groß werden.
Ausweg: Formuliere r qsort um, so dass Rekursion nur für
das kleinere der beiden Teilarray angewendet wird.
Das größere wird in einer Iteration weiterbehandelt.
Effekt: Deutlich weniger Prozeduraufrufe.
Rekursionsstack hat höchstens Tiefe log n – fast in situ“.
”
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
32
Beispiel:
4
5
12
12
10
B
3
E
5
12
17
3
AuD – 16.06.2008
F
G
4
Hier nur interne Knoten relevant.
Blätter: Knoten ohne (interne) Kinder.
FG KTuEA, TU Ilmenau
33
12
12
3
12
17
max:
10
AuD – 16.06.2008
Ein Binärbaum T mit Knotenbeschriftungen m(v) aus (U, <)
heißt min-heapgeordnet (oft: heapgeordnet),
wenn für jeden Knoten v gilt:
w Kind von v ⇒ m(v) ≤ m(w).
Heap-Ordnung in Binärbaumen:
3
FG KTuEA, TU Ilmenau
Definition
Heapsort
min:
Prozedur hr qsort(a, b) (∗ Halb-rekursive Variante ∗)
(1)
a ← a; b ← b;
(2)
while a < b do
(3)
s ← ein Element von {a, . . . , b};
(4)
vertausche A[a] und A[s];
(5)
partition(a, b, p);
(6)
if p − a < b − p then
(7)
if a < p − 1 then hr qsort(a, p − 1);
(8)
a ← p + 1;
(∗ while-Schleife bearbeitet rechtes Teilarray weiter ∗)
(9)
else (∗ p − a ≥ b − p ∗)
(10)
if p + 1 < b then hr qsort(p + 1, b);
(11)
b ← p − 1;
(∗ while-Schleife bearbeitet linkes Teilarray weiter ∗)
N
34
FG KTuEA, TU Ilmenau
E
P
H
F
N
AuD – 16.06.2008
35
Ein linksvollständiger Binärbaum:
Alle Levels j = 0, 1, . . . voll (jeweils 2j Knoten) bis auf die
tiefste.
Das tiefste Level l hat von links her gesehen eine ununterbrochene Folge von Knoten.
B
E
F
G
H
E
N
P
F
N
NB: T heapgeordnet ⇒ in Knoten v steht der minimale
Eintrag des Teilbaums Tv mit Wurzel v.
(T heißt max-heapgeordnet, wenn für jeden Knoten v gilt:
w Kind von v ⇒ m(v) ≥ m(w).)
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
Können gut als Arrays dargestellt werden!
36
Nummerierung von Knoten in unendlichem, vollständigen
Binärbaum gemäß Breitendurchlauf-Anordnung:
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
37
Nummerierung von Knoten in unendlichem, vollständigen
Binärbaum gemäß Breitendurchlauf-Anordnung:
1
1
0
5
8
0
0
1
0
0
11
10
9
1
7
6
1
0
1
12
0
1
14
13
1
0
1
0
1
0
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
0
32
FG KTuEA, TU Ilmenau
1
1
46 47
63
0
AuD – 16.06.2008
5
0
1
0
7
6
1
0
11
10
9
1
1
0
0
1
8
15
0
3
1
4
0
1
0
1
2
0
1
0
0
1
1
0
3
1
4
0
1
2
0
Beispiel:
(101110)2 = 46
1
0
1
12
0
1
14
13
1
0
1
0
1
0
15
0
1
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
0
32
38
FG KTuEA, TU Ilmenau
1
1
46 47
63
0
AuD – 16.06.2008
38
Ist s1 · · · sl ∈ {0, 1}l die Markierung auf dem Weg von der
Wurzel zu Knoten i auf Level l, so ist 1s1 · · · sl die Binärdarstellung von i.
Knoten
Knoten
Knoten
Knoten
Definition
Ein (Teil-)Array A[1 . . k] mit Einträgen aus (U × D, <)
heißt ein Min-Heap (oder Heap), wenn der entsprechende
linksvollständige Binärbaum mit k Knoten
(Knoten i mit A[i] beschriftet) (min-)heapgeordnet ist.
1 ist die Wurzel;
2i ist linkes Kind von i;
2i + 1 ist rechtes Kind von i;
i ≥ 2 hat Vater i/2.
Beobachte: Ein linksvollständiger Binärbaum besteht aus einem Anfangsabschnitt {1, 2, . . . , n} der Knoten des unendlichen vollständigen Binärbaums (mit denselben Vater-SohnBeziehungen).
Damit: Array-Darstellung für linksvollständige Binärbaume:
Speichere Einträge in Knoten 1, . . . , n in Array A[1 . . n].
Spart den Platz für die Zeiger!
FG KTuEA, TU Ilmenau
Kombiniere Heapbedingung und Array-Darstellung:
AuD – 16.06.2008
D. h.: A[1 . . k] ist ein Heap, falls für 1 ≤ i ≤ k gilt:
2i ≤ k ⇒ A[i].key ≤ A[2i].key und
2i + 1 ≤ k ⇒ A[i].key ≤ A[2i + 1].key.
(Analog: Max-Heap (benutze Maxheap-Ordnung))
39
U = {A, B, C, . . . , Z} mit der Standardordnung.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
40
Bleibt Aufgabe: Heaps reparieren
Im Beispiel: Daten weggelassen.
1
2
3
4
5
6
7
8
9
10
Ein Min-Heap und der zugeordnete Baum (k = 10):
G
E
F
G
E
H
F
H
I
* * *
1
2
3
4
5
6
7
8
9
10
B
E
F
G
E
H
F
H
I
G
11
11
12
1
12
G
* *
2
3
E
F
1
2
E
E
G
8
8
F
5
FG KTuEA, TU Ilmenau
I
7
H
F
I
7
H
F
Abwärts-Fast-Heap“ bis auf Position 1 (Wurzel), d. h.:
”
Wenn man den Eintrag in A[1] verkleinert (hier z. B. auf C),
entsteht ein Heap.
G
AuD – 16.06.2008
6
9
H
6
9 10
H
E
G
3
4
5
4
B
41
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
42
Heaps reparieren
Heaps reparieren
11
12
1
2
3
4
5
6
7
8
9
10
G
E
F
G
E
H
F
H
I
* * *
3
4
5
6
7
8
9
10
G
E
F
G
E
H
F
H
I
* * *
1
G
G
3
E
5
6
E
G
8
2
F
?
4
F
5
43
9
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
43
Heaps reparieren
11
12
1
2
3
4
5
6
7
8
9
10
G
E
F
G
E
H
F
H
I
* * *
3
4
5
6
7
8
9
10
G
E
F
G
E
H
F
H
I
* * *
5
E
2
F
6
F
AuD – 16.06.2008
5
8
I
F
<
E
G
6
7
H
F
9
H
Vergleich des kleineren“ Kinds mit dem Fehler“-Knoten.
”
”
FG KTuEA, TU Ilmenau
3
E
4
7
H
9
H
G
<
<
4
8
1
3
E
12
2
G
?
11
1
1
G
F
Vergleich der beiden Kinder des Fehler“-Knotens.
”
Heaps reparieren
2
7
H
I
H
AuD – 16.06.2008
6
E
8
Vergleich der beiden Kinder des Fehler“-Knotens.
”
F
<
G
9
FG KTuEA, TU Ilmenau
3
E
4
7
H
I
H
12
2
1
2
11
1
I
Vergleich des kleineren“ Kinds mit dem Fehler“-Knoten.
”
”
43
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
43
Heaps reparieren
Heaps reparieren
11
12
1
2
3
4
5
6
7
8
9
10
E
G
F
G
E
H
F
H
I
* * *
3
4
5
6
7
8
9
10
E
G
F
G
E
H
F
H
I
* * *
1
E
E
3
5
6
E
G
8
2
F
G
4
3
G
F
>
5
4
7
H
F
G
6
E
>
8
9
I
H
12
2
1
2
11
1
7
H
F
9
I
H
Vertauschen des kleineren“ Kinds mit dem Fehler“-Knoten.
”
”
Abwärts-Fast-Heap, Fehler in Knoten ein Level tiefer.
Vergleich des kleineren“ Kinds mit dem Fehler“-Knoten.
”
”
FG KTuEA, TU Ilmenau
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
43
Heaps reparieren
AuD – 16.06.2008
43
Heaps reparieren
11
12
1
2
3
4
5
6
7
8
9
10
E
E
F
G
G
H
F
H
I
* * *
3
4
5
6
7
8
9
10
E
E
F
G
G
H
F
H
I
* * *
1
E
2
F
E
5
G
G
8
E
3
4
6
3
F
E
5
4
7
H
F
G
G
8
9
H
12
2
1
2
11
1
I
7
H
F
9
H
Vertauschen des kleineren“ Kinds mit dem Fehler“-Knoten.
”
”
Abwärts-Fast-Heap, Fehler in Knoten ein Level tiefer.
6
I
Kein Fehler mehr: Reparatur beendet.
Fehler“-Knoten hat keine Kinder.
”
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
43
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
43
Prozedur bubbleDown(1, k) (∗ Heapreparatur in A[1 . . k] ∗)
(∗ Ausgangspunkt: A[1 . . k] ist Abwärts-Fast-Heap, möglicher Fehler in A[1]∗)
(1) j ← 1; m ← 2; done ← false;
(2) while not done and m + 1 ≤ k do
(∗ Abwärts-Fast-Heap, möglicher Fehler in A[j], A[j] hat 2 Kinder ∗)
(3)
if A[m+1].key < A[m].key
(4)
then (∗ A[m]: kleineres“ Kind ∗)
”
(5)
m ← m + 1;
(6)
if A[m].key < A[j].key
(7)
then (∗ Abwärts-Fast-Heap, möglicher Fehler in A[j] ∗)
(8)
vertausche A[m] mit A[j]; j ← m; m ← 2 ∗ j;
(9)
else (∗ fertig, kein Fehler mehr ∗)
(10)
done ← true;
(11) if not done then
(∗ Abwärts-Fast-Heap, möglicher Fehler in A[j], maximal ein Kind ∗)
(12) m ← 2 ∗ j;
(13) if m ≤ k then
(14)
if A[m].key < A[j].key
(15)
then vertausche A[m] mit A[j];
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
44
Korrektheit: Wenn vor dem Aufruf von bubbleDown(1, k)
das Teilarray A[1 . . k] ein Abwärts-Fast-Heap war, mit möglichem Fehler in A[1], d. h.:
es gibt einen Schlüssel x ≤ A[1].key derart, dass durch
Ersetzen von A[1].key durch x ein Heap entsteht,
dann ist nachher A[1 . . k] ein Heap.
Beweis: Man zeigt folgende Behauptung durch Induktion
über die Schleifendurchläufe:
(IBj )
Zu Beginn des Durchlaufs, in dem j die
Zahl j enthält, ist A[1 . . k] ein AbwärtsFast-Heap mit möglichem Fehler in A[j].
Der I.A. ist klar.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
45
Betrachte Schleifendurchlauf, in dem j die Zahl j enthält.
Nur A[2j].key kann im Unterbaum unter A[2j] zu groß sein.
1. Fall: In Zeile (8) bzw. (15) wird vertauscht.
Also: Abwärts-Fast-Heap mit möglichem Fehler in A[2j], das
ist (IB2j ).
Wir betrachten den Fall
2j + 1 ≤ k und A[m+1].key ≥ A[m].key, also m = 2j.
(Andere Fälle analog.)
2. Fall: In Zeile (8) bzw. (15) wird nicht vertauscht.
Nach der Vertauschung, vor der Änderung von j, gilt:
A[j].key ≤ A[2j].key ≤ A[2j + 1].key
(wegen der Bestimmung von m).
Weiter: Falls j > 1, gilt A[j].key ≥ A[j/2].key, wegen
der Definition von Abwärts-Fast-Heap“
”
(vor der Vertauschung war A[m].key ≥ A[j/2].key).
Weiter: Im Unterbaum unter dem größeren“ Kind A[2j + 1]
”
ist alles unverändert, also ist dort die Heapbedingung erfüllt.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
46
Dann gilt: Die Schleife endet.
A[j].key ≤ A[2j].key, A[2j + 1].key (falls 2j + 1 ≤ k).
Es muss auch A[j].key ≥ A[j/2].key gelten, wegen der
Definition von Abwärts-Fast-Heap“ (A[j].key kann nicht
”
zu klein“ sein).
”
Also ist A[1 . . k] ein Heap.
In jedem Schleifendurchlauf wird der Inhalt von j immer
mindestens verdoppelt; daher terminiert die Schleife.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
47
Kosten:
Angenommen, Array A[1 . . n] ist heapgeordnet.
. . . von Aufruf bubbleDown(1, k):
Dann können wir A[1 . . n] wie folgt fallend sortieren:
O(1) +
Θ(Anzahl der Schlüsselvergleiche A[.].key < A[.].key“).
”
Vk : Anzahl der Schlüsselvergleiche im schlechtesten Fall.
Für k = n, n − 1, n − 2, . . . , 2:
In jedem Schleifendurchlauf 2 Vergleiche.
(∗ Das minimale Element von A[1 . . k] ist A[1]! ∗)
Vertausche A[1] mit A[k].
(∗ Repariere in A[1 . . k − 1] die Heapeigenschaft: ∗)
jr : Inhalt von j in Runde r = 1, 2, 3, . . .. Verdopplung!
Falls k ≥ 3: bubbleDown(1, k − 1);
j1 = 1 = 20, jr ≥ 2r−1.
Vergleiche finden nur statt, falls k ≥ 2·jr ≥ 2r , d. h. r ≤ log k.
⇒ Vk ≤ 2 · log k.
Klar: Die Elemente im Array werden in steigender Reihenfolge
nach A[n], A[n − 1], . . . , A[2] geschrieben;
das maximale Element landet schließlich in A[1].
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
48
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
49
. . . und wie verwandelt man A[1 . . n] in einen Heap?
Prozedur HeapSelect(1, n)
(1) for k from n downto 3 do
(2)
vertausche A[1] mit A[k];
(3)
bubbleDown(1, k − 1);
(4) vertausche A[1] mit A[2].
Anzahl Vergleiche: ≤ 2≤k≤n Vk < 2n log n.
Kosten:
2≤k≤n(O(1) + O(log k)
Beispiel: Teil eines linksvollständigen Binärbaums, der einem
Teilheap“ A[3 . . 10] entspricht:
”
Teilheap
D P A B R MN K L T E F G
D
= O(n) + O(n log n) = O(n log n).
A
P
R
B
K
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
50
FG KTuEA, TU Ilmenau
L
T
AuD – 16.06.2008
N
M
E
F
G
51
Definition
Das Teilarray A[ . . k] von A[1 . . n] (1 ≤ ≤ k ≤ n)
heißt ein Teilheap, falls innerhalb“ des Teilarrays die
”
Heapeigenschaft gilt, d. h. wenn für alle i mit ≤ i ≤ k gilt:
HE(i, k) :⇔
2i ≤ k
2i + 1 ≤ k
⇒ A[i] ≤ A[2i] und
⇒ A[i] ≤ A[2i + 1]
AuD – 16.06.2008
Kann mit einer Variante von bubbleDown Heapbedingung
herstellen!
Prozedur bubbleDown(, k) (∗ Heapreparatur in T,k ∗)
j ← ; m ← 2*; done ← false;
(2) while not done and m + 1 ≤ k do
(3) . . .
(1)
Klar: Ein Teilheap ist die disjunkte Vereinigung von heapgeordneten Bäumen.
FG KTuEA, TU Ilmenau
Idee: Wenn A[ + 1 . . k] ein Teilheap ist, dann ist in dem
Teilbaum T,k mit Wurzel und Knoten in {, . . . , k} die
Heapbedingung höchstens in j verletzt.
52
Korrektheit: Wie bei bubbleDown(1, k).
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
53
Prozedur makeHeap(1, n)
(∗ Transformiert Array A[1 . . n] in Heap ∗)
(1) for l from n/2 downto 1 do
(2)
(∗ stelle in A[l..n] Heapbedingung her! ∗)
(3)
bubbleDown(l, n);
Laufzeit: O(1) plus Θ(Anzahl Vergleiche).
V,k : Anzahl der Schlüsselvergleiche im schlechtesten Fall.
In jedem Schleifendurchlauf 2 Vergleiche.
jr : Inhalt von j in Runde r = 1, 2, 3, . . ..
j1 = . Wegen Verdopplung: jr ≥ 2r−1 · .
Korrektheit:
Mit Induktion über = n/2, n/2 − 1, . . . , 2, 1 zeige:
Nach Durchlauf mit in l ist A[ . . n] Teilheap.
r
Vergleiche nur, falls k ≥ 2 · jr ≥ 2 , d. h. r ≤ log(k/).
⇒ V,k ≤ 2 · log(k/).
Ind.-Anfang: Vor der ersten Runde.
In A[n/2 + 1 . . n] gibt es keine Vater-Kind-Paare; dieses
Teilarray ist also auf jeden Fall Teilheap.
(In jedem Schleifendurchlauf maximal 2 Vergleiche.)
Ind.-Schritt: Folgt aus Korrektheit von bubbleDown(l, n).
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
54
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
55
Kosten: Anzahl der Vergleiche:
V,n ≤ 2 ·
1≤≤n/2
= 2·
Algorithmus heapSort(A[1 . . n])
(1) makeHeap(1, n);
(2) HeapSelect(1, n);
log(n/)
1≤≤n/2
1
Satz
1≤≤n/2 j : ·2j ≤n
= 2·
Algorithmus heapSort sortiert das Array A[1 . . n] in fallende
Reihenfolge.
1
1≤j≤log n 1≤≤n/2j
Die gesamte Laufzeit ist O(n log n).
n
1
≤ 2·
<
2n
·
j
2
2j
1≤j≤log n
1≤j≤log n
(∗ Geometrische Reihe: j≥1 2−j = 1. ∗)
Die Gesamtzahl der Vergleiche ist kleiner als 2n(log n + 1),
für genügend große n.
< 2n.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
56
Beweis: Korrektheit und Zeitbedarf folgt aus der Analyse der
Teilprozeduren.
Gesamtzahl der Vergleiche:
2n + 2≤k≤n Vk ≤ 2n + 2 · 2≤k≤n log k
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
57
Bemerkung 1: Wenn Sortierung in aufsteigende Reihenfolge
gewünscht wird, ersetze in allen Prozeduren und Programmen
Schlüsselvergleiche A[i] ≤ A[j]“ durch A[i] ≥ A[j]“ und
”
”
umgekehrt.
Die in A[1 . . n] entstehende Struktur heißt ein Max-Heap
— in der Wurzel des Binärbaumes steht immer das Maximum.
≤ 2n + 2n log n.
Bemerkung 2: Mittlere Anzahl von Vergleichen, unter der Annahme,
dass jede Anordnung der Eingabeobjekte in
A[1 . . n] dieselbe Wahrscheinlichkeit 1/n! hat, ist ≈ 2n log n.
Bem.: Kann sogar zeigen: < 2n log n Vergleiche.
Bemerkung 3: Variante Bottom-Up-Heapsort“ hat
”
n log n + O(n) Vergleiche im mittleren Fall. – Idee: Bei bubbleDown
laufe gesamten Weg der kleineren Söhne“ bis zum Blatt, füge das kritische
”
Element aus der Wurzel von unten nach oben an die richtige Stelle ein.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
58
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
59
Bemerkung 4: Bei Variante 4-Way-Heapsort“ hat Knoten 1
”
die Kinder 2, 3, . . . , 7 und jeder Knoten i ≥ 2 die 4 Kinder: 4i,
4i + 1, 4i + 2, 4i + 3.
Die Heapbedingung wird verallgemeinert:
Datenstruktur: Priority Queues
oder
Vorrangswarteschlangen
A[1] ≤ A[2], . . . , A[7],
A[i] ≤ A[4i], A[4i + 1], A[4i + 2], A[4i + 3], i ≥ 8.
Idee: (Daten mit) Schlüssel aus sortiertem Universum (U, <)
werden eingefügt und entnommen.
Bei bubbleDown: Vertauschen mit dem kleinsten KindEintrag (Auswahl aus 4).
Beim Entnehmen wird immer der Eintrag mit dem kleinsten
Schlüssel gewählt.
Vorteil: Baum hat nur Tiefe log4 n = 12 log n.
Zugriff nur auf
schneller.
FG KTuEA, TU Ilmenau
1
2
log n Bereiche im Array: cache-freundlicher,
AuD – 16.06.2008
60
Schlüssel =
Prioritäten“
”
– je kleiner der Schlüssel, desto höher die Priorität –
Wähle stets den Eintrag mit höchster Priorität.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
61
Anwendungen:
Anwendungen:
1) Prozessverwaltung in Multitask-System
2) Discrete Event Simulation“
”
System von Aktionen soll auf dem Rechner simuliert werden.
Jeder ausführbaren Aktion A ist ein Zeitpunkt tA ∈ [0, ∞)
zugeordnet.
Jeder Prozess hat einen Namen (eine ID“) und eine Priorität
”
(Zahl in N).
(Üblich: kleinere Zahlen bedeuten höhere Prioritäten.)
Aktuell rechenbereite Prozesse befinden sich in der Prozesswarteschlange. Bei Freiwerden eines Prozessors (z. B. durch
Unterbrechen eines Prozesses) wird einer der Prozesse mit
höchster Priorität aus der Warteschlange entnommen und
weitergeführt.
Die Ausführung einer Aktion A kann neue Aktionen A erzeugen oder ausführbar machen, mit neuen Zeitpunkten tA > tA.
Ein Schritt: Wähle diejenige noch nicht ausgeführte Aktion
mit dem frühesten Ausführungszeitpunkt und führe sie aus.
3) Innerhalb von Algorithmen (Dijkstra, Jarnı́k/Prim: später).
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
62
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
63
Beispiel: Datensatz: (n, L), n ∈ N,
L ∈ {A, . . . , Z}, Schlüssel ist der Buchstabe L.
Bei Priority Queues zu berücksichtigen:
Mehrere Objekte mit derselben Priorität sind möglich,
die man nicht identifizieren darf.
Mehrere identische Objekte sind möglich,
die man nicht identifizieren darf:
Einträge sind nicht blanke“ Schlüssel, sondern
”
mit Prioritäten versehene Datensätze.
Beim Entnehmen: Alle Exemplare des kleinsten Schlüssels
gleichberechtigt, einer gewinnt.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
64
Operation
empty
insert(1, D)
insert(2, B)
insert(3, D)
insert(4, E)
insert(5, G)
extract min
extract min
insert(4, E)
insert(2, D)
extract min
isempty
innerer Zustand
∅
(1, D)
{(1, D), (2, B)}
{(1, D), (2, B), (3, D)}
{(1, D), (2, B), (3, D), (4, E)}
{(1, D), (2, B), (3, D), (4, E), (5, G)}
{(1, D), (3, D), (4, E), (5, G)}
{(1, D), (4, E), (5, G)}
{(4, E), (1, D), (2, B), (3, D), (4, E)}
{(4, E), (2, D), (1, D), (4, E), (5, G)}
{(4, E), (1, D), (4, E), (5, G)}
{(4, E), (1, D), (4, E), (5, G)}
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
Datentyp: SimplePriorityQueue (Einfache Vorrangswarteschlange)
Mathematisches Modell:
Signatur:
Sorten:
Keys
:
Data
:
Boolean :
PrioQ
:
Operationen:
Sorten:
Keys
Data
PrioQ (∗ Priority Queues“ ∗)
”
Boolean
Operatoren:
empty : → PrioQ
isempty : PrioQ → Boolean
insert : Keys × Data × PrioQ → PrioQ
extract min: PrioQ → Keys × Data × PrioQ
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
empty ( ) = ∅
isempty (P ) =
66
FG KTuEA, TU Ilmenau
Ausgabe
–
–
–
–
–
–
(2, B)
(3, D)
–
–
(2, D)
false
65
(U, <) (∗ totalgeordnetes Universum“ ∗)
”
D (∗ Menge von Datensätzen“ ∗)
”
{false, true}
die Menge aller endlichen Multimengen P ⊆ U × D
(∗ die leere Multimenge ∗)
true, für P = ∅
false, für P = ∅
AuD – 16.06.2008
67
insert(x, d, P ) = P ∪ {(x, d)} (als Multimenge),
extract min(P ) =
undefiniert,
Implementierung: Neben Array A[1 . . m]:
Pegel k: Aktuelle Zahl k von Einträgen.
für P = ∅
Überlaufprobleme werden ausgeklammert.
(Verdoppelungsstrategie, falls nötig.)
(x0, d0, P ), für P = ∅,
wobei im zweiten Fall
(x0, d0) ein Element in P ist mit
x0 = min{x | ∃d : (x, d) ∈ P },
und P := P − {(p0, x0)}
(als Multimenge).
Standardimplementierung: (Binäre) Heaps
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
68
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
69
empty(m):
Lege Array A[1 . . m] an;
Jeder Eintrag ist ein Paar (key, data).
extractMin: Implementierung von extract min(P ):
k ← 0.
Entnehme A[1] (hier B“) und gib es aus.
”
Ein Eintrag mit minimalem Schlüssel steht in der Wurzel, d. h.
in Arrayposition 1.
(∗ Zeitaufwand: O(1) oder O(m) ∗)
1
2
3
4
5
6
7
8
9
10
B
E
F
G
E
H
F
H
I
G
isempty():
return(k = 0);
11
12
* *
1
B
2
(∗ Zeitaufwand: O(1) ∗)
3
E
F
4
5
E
G
8
6
7
H
F
9 10
H
I
G
Loch“ im Binärbaum.
”
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
70
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
71
extractMin: Implementierung von extract min(P ):
extractMin: Implementierung von extract min(P ):
Ein Eintrag mit minimalem Schlüssel steht in der Wurzel, d. h.
in Arrayposition 1.
Ein Eintrag mit minimalem Schlüssel steht in der Wurzel, d. h.
in Arrayposition 1.
Entnehme A[1] (hier B“) und gib es aus.
”
Entnehme A[1] (und gib es aus).
1
2
3
4
5
6
7
8
9
10
?
E
F
G
E
H
F
H
I
G
11
12
* *
3
4
5
6
7
8
9
10
G
E
F
G
E
H
F
H
I
* * *
1
G
?
3
E
5
6
E
G
8
2
F
4
71
11
3
4
5
6
7
8
9
10
G
E
F
G
E
H
F
H
I
* * *
1
G
3
E
F
5
8
6
7
H
F
9
H
AuD – 16.06.2008
I
*
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
72
Korrektheit: klar wegen Korrektheit von bubbleDown(1, k).
I
Zeitaufwand: O(log(k)).
Abwärts-Fast-Heap“ bis auf Position 1 (Wurzel), d. h.:
”
bubbleDown hilft hier!
FG KTuEA, TU Ilmenau
F
Prozedur extractMin
(∗ Entnehmen eines minimalen Eintrags aus Priority-Queue ∗)
(∗ Ausgangspunkt: Pegel k, A[1 . . k] ist Heap, k ≥ 1 ∗)
(1) x ← A[1].key; d ← A[1].data;
(2) A[1] ← A[k];
(3) k--;
(4) if k > 1 then bubbleDown(1, k);
(5) return (x, d);
12
2
E
7
H
Loch“ im Binärbaum – stopfen“ mit dem letzten Eintrag.
”
”
1
4
6
9 10
H
Bleibt Aufgabe: Heap reparieren
G
5
E
8
G
AuD – 16.06.2008
2
F
G
F
Loch“ im Binärbaum.
”
FG KTuEA, TU Ilmenau
3
E
4
7
H
9 10
I
H
12
2
1
2
11
1
73
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
74
Implementierung von insert(x, d):
Heap:
Voraussetzung: A[1..k] ist Heap; 1 ≤ i ≤ k < m.
k++;
A[k] ← (x, d).
1
2
3
4
5
6
7
8
9
C
E
F
G
J
K
F
H
L
Wir nennen A[1..k] einen Aufwärts-Fast-Heap“ mit
”
möglicher Fehlerstelle k.
Heißt: Es gibt einen Schlüssel y ≥ A[k].key, so dass ein Heap
entsteht, wenn man A[k].key durch y ersetzt.
AuD – 16.06.2008
75
Einfügen von D“ an Stelle k = 11.
”
2
3
4
5
6
7
8
9
C
E
F
G
J
K
F
H
L
2
3
E
F
4
5
G
6
J
8
9 10
H
L
FG KTuEA, TU Ilmenau
Q
AuD – 16.06.2008
10
Q
11
D
12
76
*
1
2
3
4
5
6
7
8
9
C
E
F
G
J
K
F
H
L
3
5
9 10
FG KTuEA, TU Ilmenau
2
6
J
L
AuD – 16.06.2008
Q
11
D
12
*
3
E
F
4
7
K
5
G
F
9 10
H
D
76
FG KTuEA, TU Ilmenau
6
J
8
11
Q
10
C
F
G
F
1
E
4
7
K
C
H
* *
C
1
8
12
Heapreparatur bei Aufwärts-Fast-Heaps: bubbleUp.
1
2
Q
11
1
An der Stelle k ist nun eventuell die Heapeigenschaft gestört
(x zu klein).
FG KTuEA, TU Ilmenau
10
L
7
K
F
11
Q
AuD – 16.06.2008
D
76
Heapreparatur bei Aufwärts-Fast-Heaps: bubbleUp.
1
2
3
4
5
6
7
8
9
C
E
F
G
D
K
F
H
L
10
11
Q
J
Heapreparatur bei Aufwärts-Fast-Heaps: bubbleUp.
12
*
1
2
3
4
5
6
7
8
9
C
E
F
G
D
K
F
H
L
1
E
5
6
9 10
H
L
FG KTuEA, TU Ilmenau
5
76
Heapreparatur bei Aufwärts-Fast-Heaps: bubbleUp.
1
2
3
4
5
6
7
8
9
C
D
F
G
E
K
F
H
L
10
11
Q
J
L
FG KTuEA, TU Ilmenau
J
AuD – 16.06.2008
76
Heapreparatur bei Aufwärts-Fast-Heaps: bubbleUp.
12
*
1
2
3
4
5
6
7
8
9
C
D
F
G
E
K
F
H
L
3
6
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
J
*
F
5
G
F
76
FG KTuEA, TU Ilmenau
L
7
K
9 10
H
J
6
E
8
11
Q
Q
12
3
D
4
7
K
9 10
L
2
F
5
H
11
C
D
E
8
10
1
C
G
F
11
Q
1
4
7
K
9 10
H
J
6
D
8
AuD – 16.06.2008
2
*
F
G
F
11
Q
J
3
E
4
7
K
D
8
2
F
G
Q
12
C
3
4
11
1
C
2
10
F
11
Q
AuD – 16.06.2008
J
76
Prozedur bubbleUp(i) (∗ Heapreparatur ab A[i] nach oben ∗)
(1) j ← i;
(2) h ← j ÷ 2;
(3) x ← A[j].key; d ← A[j].data;
(4) while h ≥ 1 and x < A[h].key do
(5)
A[j] ← A[h];
(6)
j ← h;
(7)
h ← j/2;
(8) A[j] ← (x, d);
Heapreparatur bei Aufwärts-Fast-Heaps: bubbleUp.
1
2
3
4
5
6
7
8
9
C
D
F
G
E
K
F
H
L
10
11
Q
J
12
*
1
C
2
3
D
F
4
5
G
6
8
9 10
H
L
7
K
E
F
11
Q
Auf dem Weg von A[i] zur Wurzel werden alle Elemente,
deren Schlüssel größer als x (= der neue Eintrag in A[i]) ist,
um eine Position (auf dem Weg) nach unten gezogen.
J
Eintrag A[i] landet in der freigewordenen Position.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
76
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
Behauptung
1
2
3
4
5
6
7
8
9
C
D
F
G
E
K
F
H
L
Wenn A[1..k] ein Aufwärts-Fast-Heap ist mit möglichem
Fehler an Stelle i,
10
11
Q
J
12
*
1
C
2
dann ist nach Ausführung von bubbleUp(i) das Array
A[1..k] ein Heap.
Der Zeitaufwand ist O(log i), die Anzahl der Schlüsselvergleiche ist höchstens das Level von i im Baum, also die Anzahl
der Bits in der Binärdarstellung bin(i), d.h. log(i + 1).
77
3
D
F
4
5
G
6
8
9 10
H
L
7
K
E
F
11
Q
J
Beweis: Wenn sich ein Eintrag ändert, dann liegt er auf dem
Weg von i zur Wurzel.
Das Hochziehen“ von A[i] wirkt wie bei Sortieren durch
”
Einfügen:
nach bubbleUp(i) sind die Elemente auf diesem Weg (fallend)
geordnet.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
78
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
79
1
2
3
4
5
6
7
8
9
C
D
F
G
E
K
F
H
L
10
11
Q
J
12
Prozedur insert(x, d)
(∗ Einfügen eines neuen Eintrags in Priority-Queue ∗)
(∗ Ausgangspunkt: Pegel k, A[1 . . k] ist Heap, k < n ∗)
(1) if k = m then Überlauf-Fehler“;
”
(2) k++;
(3) A[k] ← (x, d);
(4) bubbleUp(k).
*
1
C
2
3
D
F
4
5
G
6
8
9 10
H
L
7
K
E
F
11
Q
J
Falls A[j] durch A[j/2] ersetzt wird, ist nachher A[j]
kleiner als seine beiden Kinder.
Korrektheit: klar wegen Korrektheit von bubbleUp(k).
Zeitaufwand: O(log(k)).
Beide Kinder der Position , an die x gelangt, enthalten
Schlüssel, die ≥ x sind: der Schlüssel auf dem Weg (vorher
in A[]), weil er mit x verglichen wird; der Schlüssel im Kind
von neben dem Weg, weil er ≥ dem alten Wert A[] ist.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
80
Wir können bubbleUp(i) sogar für eine etwas allgemeinere
Operation verwenden:
Wir ersetzen einen beliebigen Schlüssel im Heap (Position i)
durch einen kleineren.
Mit bubbleUp(i) kann die Heapeigenschaft wieder hergestellt
werden.
Prozedur decreaseKey(x, i)
(∗ (Erniedrigen des Schlüssels an Arrayposition i auf x) ∗)
(1) if A[i].key < x then Fehlerbehandlung;
(2) A[i].key ← x;
(3) bubbleUp(i).
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
81
SimplePriorityQueue plus decreaseKey“: Priority Queue.
”
Satz
Der Datentyp “Priority Queue” kann mit Hilfe eines Heaps
implementiert werden.
Dabei erfordern empty und isempty (und das Ermitteln des
kleinsten Eintrags) konstante Zeit (bzw. O(m), wenn ein Array der
Größe m initialisiert werden muss)
und insert, extractMin und decreaseKey benötigen jeweils
Zeit O(log n).
Dabei ist n jeweils der aktuelle Pegelstand, also die Anzahl
der Einträge in der Priority Queue.
(∗ Zeitaufwand: O(log(i)) ∗)
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
82
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
83
Sei nun A ein Schlüsselvergleichsverfahren.
Untere Schranke für Sortieren
Wir wenden A auf n! verschiedene Eingaben
Sortieralgorithmus A heißt ein Schlüsselvergleichsverfahren,
wenn
in A auf Schlüssel x, y aus U nur Operationen
x≤y ?
x<y ?
(a1, 1), . . . , (an, n)
an, wobei (a1, . . . , an) eine Permutation π von {1, . . . , n} ist.
x=y ?
sowie Verschieben und Kopieren angewendet werden.
Sortierschlüssel: erste Komponente.
Schlüssel sind atomare Objekte“.
”
Mergesort, Quicksort, Heapsort, Shellsort, Insertion-Sort,
Bubblesort sind Schlüsselvergleichsverfahren.
Beispiel: Aus (2, 1), (4, 2), (1, 3), (3, 4) wird (1, 3), (2, 1), (3, 4), (4, 2).
Effekt: In den zweiten Komponenten steht die Permutation
π −1, die die Eingabe (a1, . . . , an) sortiert.
D.h.: n! verschiedene
Eingabepermutation.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
84
Beispiel: Mergesort mit 3 Elementen
a 1< a 2
ja
Start
0
nein
231
213
0
a 2< a 3
312
ja
nein
AuD – 16.06.2008
a i3< a j3
1
0
1
132
a i6< a j6
a i5< a j5
1
0
ja
1
0
a i7< a j7
1
0
π’’
1
π’’’
a i20< a j20
123
0
π
In Blättern: die zweiten Komponenten
FG KTuEA, TU Ilmenau
1
ja
nein
a 1< a 3
a i1< a j1
a i2< a j2
0
a i4< a j4
321
jede
85
a3
a2
a 1< a 3
nein
für
AuD – 16.06.2008
ja
a 2< a 3
eine
Abstraktion: Vergleichsbaum TA,n
a1
nein
FG KTuEA, TU Ilmenau
Ausgaben,
86
FG KTuEA, TU Ilmenau
1
π’
AuD – 16.06.2008
87
Satz
Bemerkung:
Wenn A ein Schlüsselvergleichsverfahren ist, das das Sortierproblem löst,
n! ≥ (n/e)n.
dann gibt es eine Eingabe (a1, . . . , an), auf der A mindestens
log(n!) Vergleiche ausführt.
Beweis: Der Vergleichsbaum, den A erzeugt, hat n! (externe)
Blätter, also hat er Tiefe ≥ log(n!).
Die worst-case Vergleichsanzahl ist eine natürliche Zahl, also
ist sie ≥ log(n!).
Beweis: nn/n! <
Also: n! ≥ (n/e)n.
AuD – 16.06.2008
88
ni
i≥0 i!
= en.
Also: log(n!) ≥ log((n/e)n) = n log n − n log e = n log n −
1,44269..n.
Erinnerung (siehe Mergesort-Analyse): n! ≤ (en)(n/e)n.
Mitteilung: Stirlingsche Formel
Für n ≥ 1 gilt:
√
FG KTuEA, TU Ilmenau
FG KTuEA, TU Ilmenau
2πn
n n
e
≤ n! ≤
√
2πn
n n
e
1
e 12n
AuD – 16.06.2008
89
Satz
Binärbaum-Fakten 3
Wenn A ein Schlüsselvergleichsverfahren ist, das das Sortierproblem löst,
0
dann gilt für die mittlere Vergleichsanzahl Vn
(gemittelt über die n! verschiedenen Eingabeanordnungen):
0
0
C
Vn ≥ log(n!).
Beweis: Der Vergleichsbaum, den A erzeugt, hat n! (externe)
Blätter.
Aus den Betrachtungen zu Binärbäumen:
1
1
0
H
1
1
F
0
G
0
E
1
0
1
0
B
1
I
0
A
1
D
1
K
Proposition
T hat N externe Knoten ⇒ TEPL(T ) ≥ N log N .
Mittlere äußere Weglänge ist ≥ log(N ). Dies wenden wir
auf den Vergleichsbaum (mit N = n!) an:
Mittlere Anzahl von Vergleichen ist ≥ log(n!).
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
90
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
91
Satz
Wenn A ein Schlüsselvergleichsverfahren ist, das das Sortierproblem löst und dabei Randomisierung benutzt
(d.h. Zufallsexperimente ausführt, z.B. Randomisiertes
Quicksort)
dann gibt es eine Eingabe a = (a1, . . . , an) aus verschiedenen
Elementen von U ,
so dass die erwartete Vergleichsanzahl von A auf a
(gemittelt über die Zufallsexperimente von A)
≥ log(n!) ist.
Beweis: Fortgeschritten, hier weggelassen.
FG KTuEA, TU Ilmenau
AuD – 16.06.2008
92
Herunterladen