Algorithmen & Datenstrukturen Prof. Dr. Wolfgang Schramm
SUCHEN UND SORTIEREN
Kapitel 5
1
Übersicht
1. Einführung 2. Algorithmen
3. EigenschaDen von
Programmiersprachen 4. Algorithmenparadigmen 5. Suchen & SorLeren
6. Hashing
7. Komplexität von Algorithmen 8. Abstrakte Datentypen (ADT) 9. Listen
10. Bäume
11. Graphen
2
Lernziele des Kapitels
¨
Kennenlernen von Suchstrategien?
¨
Verstehen warum man vor der Suche besser sorLert.
¨
Kennenlernen verschiedener SorLeralgorithmen und ihrer speziellen EigenschaDen.
¨
Verstehen, warum verschiedene SorLeralgorithmen entwickelt wurden.
¨
Vergleich und Bewertung der verschiedenen
SorLeralgorithmen.
2
3
Inhalt
o
Suchalgorithmen
o
Aufwand für das Suchen
o
Grundlegende SorLeralgorithmen
o
EigenschaDen von SorLeralgorithmen
o
Ein paar besondere SorLeralgorithmen
o
Vergleich der SorLeralgorithmen
o
Laufzeitverhalten der SorLeralgorithmen
o
Die Algorithmen werden in Pseudocode und in Java bzw. in Mischformen
angegeben.
4
Suchen – in sorLerten Folgen
o
Voraussetzung: Arrays in Java sind bekannt.
o
Eine der häufigsten Aufgaben in der InformaLk: Suchen von Daten.
o
Der einfachste Fall: Suchen in sorLerten Folgen.
o
Vereinfachende Annahmen:
¤
Folge = Feld von numerischen Werten.
¤
Auf jedes Element der Folge F kann über den Index i zugegriffen werden (mit F[i]). Erstes Element: F[1], letztes Element bei n Werten: F[n].
¤
Für die Feldelemente sind die bekannten Vergleichsoperatoren =, <
und > definiert.
¤
Der für die Suche relevante Teil ist der numerische Wert, nach dem
gesucht wird. Der gesuchte Wert ist der Suchschlüssel.
5
Sequenzielle Suche
o
Die Folge wird sequenziell durchlaufen, beginnend beim ersten Element.
o
In jedem Schrin wird das aktuelle Element mit dem Suchschlüssel verglichen.
o
Sobald das gesuchte Element gefunden wurde, kann die Suche beendet werden.
o
Wenn man am Ende der Folge ankommt, ohne das Suchelement gefunden zu
haben, wird als Ergebnis ein spezielles Element NO_KEY zurückgegeben.
6
Sequenzielle Suche: Algorithmus
seqSearch (F, k) → p
Eingabe: Folge F der Länge n, Suchschlüssel k
Ausgabe: PosiLon p des ersten Elements aus F, das gleich k ist, sonst NO_KEY for i:= 1 to n
if F[i] = k then return i;
fi;
od
return NO_KEY
7
Aufwand
WichLgstes Kriterium für Beurteilung von Suchverfahren:
Aufwand.
Was ist das Aufwand bzw. wie wird der Aufwand berechnet?
¤
Notwendige Schrine mit den Vergleichen = Anzahl der Schleifendurchläufe.
¤
Der Aufwand wird (sinnvoller Weise) nicht absolut betrachtet, sondern in
Abhängigkeit von der Länge n der Folge.
8
Sequenzielle Suche: Aufwand
Anzahl der Vergleiche
bester Fall
schlechtester Fall
Durchschnitt (erfolgreiche Suche) Durchschnitt (erfolglose Suche)
1 n n/2
n
9
Sequenzielle Suche: Java-‐Programm 1/2
package ads_seqSearch;
import static gdi.MakeItSimple.*;
class seqSearch {
public final static int NO_KEY = -1;
static int search (int [] array, int key) { for (int i = 0; i < array.length; i++) if (array[i] == key) return i;
return NO_KEY;
}
// .... Fortsetzung nächste Seite
Such- funktion
10
Sequenzielle Suche: Java-‐Programm 2/2
// ... Fortsetzung: Hauptprogramm //
public static void main(String[] args) { int [] F = {2, 4, 5, 6, 7, 8, 9, 11};
int i, key = 1;
print("search key = ");
key = readInt();
println ("search result: " + seqSearch (F, key));
} }
Aufruf der Suchfunktion
11
Sequenzielle Suche: Java-‐Programm
package ads_seqSearch;
import static gdi.MakeItSimple.*;
public static void main(String[] args) { int [] F = {2, 4, 5, 6, 7, 8, 9, 11};
int key = 1;
int res = NO_KEY ; int i = 0;
print("search key = ");
key = readInt();
while (i < F.length && res == NO_KEY) { if (F[i] == key) res = i;
i++;
}
println ("search result: " + res);
} }
12
Binäre Suche: Beschreibung des Algorithmus
1. Wähle den minleren Eintrag und prüfe, ob gesuchter Wert in der ersten oder in der zweiten HälDe der Folge ist.
2. Fahre analog Schrin 1. mit der HälDe fort, in der sich der Eintrag befindet.
Realisierungsvarianten:
iteraLv oder rekursiv.
13
Binäre Suche: Algorithmus
binarySearch (F, k) → p
Eingabe: Folge F der Länge n, Suchschlüssel k
Ausgabe: PosiLon p des ersten Elements aus F, das gleich k ist, sonst NO_KEY u := 1; o := n;
while u <= o
m := (u+o)/2;
if F[m] = k then return m; // gefunden ! else if k < F[m] then
o := m – 1; // suche in der unteren HälDe weiter else
u := m + 1; // suche in der oberen HälDe weiter fi;
fi;
od;
return NO_KEY
14
Binäre Suche: Beispiel
0 1 2 4 5 8 9 12 13 18
u m o
0 1 2 4 5 8 9 12 13 18
u m o
0 1 2 4 5 8 9 12 13 18
o m
1 2 3 4 5 6 7 8 9 10
Suche nach Schlüssel: 8
u
15
Binäre Suche: Aufwand
Anzahl der Vergleiche
bester Fall
schlechtester Fall
Durchschnitt (erfolgreiche Suche) Durchschnitt (erfolglose Suche)
1
≈
log
2n
≈
log
2n
≈
log
2n
16
Binäre Suche: Java-‐FunkLon
static int search (int [] array, int key) { int u = 0, o = array.length - 1;
while (u <= o) {
int m = (u + o) / 2;
if (array[m] == key) return m; // gefunden !
else if (key < array[m])
o = m-1; // suche in der unteren Hälfte weiter
else u = m+1; // suche in der oberen Hälfte weiter
}
return NO_KEY;
}
17
Binäre Suche vs. sequenzielle Suche (im Minel)
≈ 13.3
≈ 9.9
≈ 6.6
≈ 3.3 binär
(log2 n)
≈ 5000
≈ 500
≈ 50
≈ 5 sequenziell
(n/2)
10.000 (10 4) 1.000 (103)
100 (102) 10
Anz. Elemente Verfahren
18
SorLeren
o
Aufgabe (in der Praxis): Ordnen von Dateien mit Datensätzen, die Schlüssel enthalten.
o
Umordnung der Datensätze, dass eine klar definierte Ordnung der Schlüssel entsteht.
o
Viele Anwendungen setzen -‐ aus unterschiedlichen Gründen -‐ sorLerte Datenmengen voraus:
¤
Kontoauszüge nach Kontonummer.
¤
Prüfungsnoten nach Matrikelnummer.
¤
Buchausleihe nach Namen.
¤
Das Suchen wird schneller.
19
SorLeren: Grundbegriffe
o
Interne Verfahren
¤
Beim SorLeren von Hauptspeicherstrukturen (z.B. Felder).
¤
Voraussetzung: die zu sorLerende Folge passt in den Hauptspeicher.
o
Externe Verfahren
¤
Wenn der Hauptspeicher nicht ausreicht.
¤
Es wird auf einem externen Speichermedium sorLert: Festplane, Magnetband.
o
Stabilität eines SorLerverfahrens
¤
Sind 2 Schlüssel gleich, dann wird die relaLve Reihenfolge beibehalten.
20
SorLeren: Stabilität -‐ Beispiel
Name Alter
Abel, Günther 65 Krämer, Willy 52 Stein, Erwin 48 Urschel, Karin 24 Winter, Gerd 52
Name Alter
Urschel, Karin 24 Stein, Erwin 48 Krämer, Willy 52 Winter, Gerd 52 Abel, Günther 65
Sortierkriterium
Sortierkriterium
Die Reihenfolge bleibt unverändert.
21
Gruppenarbeit
o
Aufgabe: SorLeren von Spielkarten (z.B.
Skatspiel – französisches Blan). Acht Karten reichen aus.
o
Ziel: Formulierung eines Algorithmus in Pseudocode zum SorLeren einer Folge von Spielkarten der Länge n.
o
Vorher: DefiniLon der Rahmen-‐
bedingungen, d.h. wodurch ist die
SorLerreihenfolge gegeben; definieren Sie
eine „größer“-‐Beziehung.
22
SorLeren durch SelekLon – SelecLon Sort
o
Vorgehensweise kann auch beim SorLeren von Spielkarten angewandt werden.
o
SorLeren eines Arrays (= Stapel):
¤
Suche das größte Element.
¤
Füge dieses Element am Ende des Stapels ein.
¤
Dies wird in jedem Schrin mit einem jeweils um 1 verkleinerten
Bereich des Stapels ausgeführt, solange bis der Stapel die Länge 1 hat.
Somit sammeln sich am Ende des Stapels die bereits sorLerten
Elemente.
23
SelecLon Sort: Algorithmus
SelecMonSort (F)
Eingabe: zu sorLerende Folge F der Länge n p := n;
while p > 1 do
g := Index des größten Elementes aus F im Bereich 1 – p;
Vertausche Werte von F[p] und F[g];
p := p-‐1;
od
24
SelecLon Sort: Beispiel
44 6 55 30 18 94 44 6 55 30 94 18
44 6 18 30 55 94
30 6 18 44 55 94
18 6 30 44 55 94
6 18 30 44 55 94
Ursprüngliche Schlüssel
Nach dem 1. Durchlauf
Nach dem 2. Durchlauf
Nach dem 3. Durchlauf
Nach dem 4. Durchlauf
Nach dem 5. Durchlauf
25
SelecLon Sort: Programm 1/2
static void SelectionSort (int [] array) { int marker = array.length - 1;
while (marker > 0) {
// Suche größtes Element
int max = 0;
// ... das ist zunächst das erste Elementfor (int i = 1; i <= marker; i++)
// Suche in Restfolgeif (array [i] > array [max])
// größeres Element gef.max = i;
swap (array, marker, max);
// Tausche array[marker] mit dem gefundenen Element
marker--;
}
}
26
SelecLon Sort: Programm 2/2
static void swap (int [] array, int idx1, int idx2)
// Hilfsmethode zum Vertauschen zweier Feldelemente
{
int tmp = array[idx1];
array[idx1] = array[idx2];
array[idx2] = tmp;
}
27
SorLeren durch Einfügen – InserLon Sort
o
Typische menschliche Vorgehensweise – etwa beim SorLeren von Spielkarten:
1. Beginne mit der ersten Karte einen neuen Stapel.
2. Nimm jeweils die nächste Karte des Originalstapels und füge diese an der richLgen Stelle in den neuen Stapel ein.
• Sortieren eines Arrays bzw. einer Folge (= Stapel):
– Das erste Element ist bereits sortiert.
– Der Zielstapel hat bis jetzt die Länge 1.
– Beginnend ab dem 2. Element wird an der passenden Stelle in den Ziel- stapel eingefügt.
– Muss ein Element an einer Stelle dazwischen geschoben werden, dann werden die rechts davon liegenden Elemente jeweils um eine Position nach rechts geschoben.
28
InserLon Sort: Algorithmus
InserMonSort (F)
Eingabe: zu sorLerende Folge F der Länge n
for i := 2 to n do // Element an PosiLon i wird an der passenden Stelle eingefügt m := F[i]; // merke einzufügendes Element
j := i;
while j > 1 do // laufe von rechts nach links
if F[j-‐1] >= m then // verschiebe F[j-‐1] eine PosiLon nach rechts
F [j] := F[j-‐1];
j := j-‐1;
else
Verlasse innere Schleife (= while-‐Schleife) // weil EinfügeposiLon gefunden fi;
od; // while-‐Schleife
F [j] :=m; // gemerktes Element an der richLgen PosiLon einfügen od; // for-‐Schleife
30
InserLon Sort -‐ Beispiel
6 44 55 30 94 18 44 6 55 30 94 18
6 44 55 30 94 18
6 30 44 55 94 18
6 30 44 55 94 18
6 18 30 44 55 94 Ursprüngliche Schlüssel
Nach dem 1. Durchlauf
Nach dem 2. Durchlauf
Nach dem 3. Durchlauf
Nach dem 4. Durchlauf
Nach dem 5. Durchlauf
sortiert
31
InserLon Sort: Programm
static void insertionSort (int [] array) { for (int i = 1; i < array.length; i++) { int j = i;
int m = array[i];
// Marker-Feldwhile (j > 0 && array [j-1] > m) {
// Verschiebe alle größeren Elemente nach hinten
array [j] = array [j-1];
j--;
}
// Setze m auf das freie Feld
array[j] = m;
}
}
32
SorLeren durch Vertauschen – Bubble Sort
o
Man vergleicht paarweise immer 2 benachbarte Elemente miteinander.
o
Entsprechen diese Elemente nicht der SorLerreihenfolge, dann vertauscht man sie.
o
Elemente, die größer sind als ihr Nachfolger überholen diese und steigen zum Ende der Folge hin.
o
Man stellt sich die sorLerende Folge verLkal angeordnet vor, dann kann man sich verschieden große, aufsteigende (LuD-‐) Blasen (bubbles) vorstellen. Wie in einer Flüssigkeit sorLeren sich diese alleine, da die größeren Blasen die kleineren
überholen.
33
Bubble Sort: 1. Algorithmus
BubbleSort (F)
Eingabe: zu sorLerende Folge F der Länge n for j := 1 to n do
for i := 1 to n-‐1 do if F[i] > F[i+1] then
Vertausche Werte von F[i] und F[i+1];
fi;
od;
od;
34
Bubble Sort: 2. Algorithmus
BubbleSort (F)
Eingabe: zu sorLerende Folge F der Länge n do
for i := 1 to n -‐1 do if F[i] > F[i+1] then
Vertausche Werte von F[i] und F[i+1];
fi;
od
while Vertauschungen staQ gefunden haben
35
Bubble Sort: 3. Algorithmus
BubbleSort (F)
Eingabe: zu sorLerende Folge F der Länge n for j := 1 to n do
for i := 1 to n -‐ j do if F[i] > F[i+1] then
Vertausche Werte von F[i] und F[i+1];
fi;
od;
od;
36
Bubble Sort: Beispiel
6 44 55 30 94 18 44 6 55 30 94 18
6 44 30 55 94 18
Ursprüngliche Schlüssel
Nach dem 1. Durchlauf
Nach dem 2. Durchlauf
Nach dem 3. Durchlauf Nach dem 4. Durchlauf
6 44 30 55 18 94
6 30 44 55 18 94
6 30 44 18 55 94
6 30 18 44 55 94
6 18 30 44 55 94
37
Bubble Sort: Programm (zu 2. Algorithmus)
static void BubbleSort (int [] array) { boolean swapped;
do {
swapped = false;
for (int i = 0; i < array.length - 1; i++) { if (array [i] > array [i+1]) {
swap (array, i, i+1);
// tausche Elementeswapped = true;
} }
} while (swapped);
// solange noch Vertauschung}
38
SorLeren durch Mischen – Merge Sort
o
Externes Verfahren – wenn Dateien nicht in den Hauptspeicher passen.
o
SorLeren in 2 Schrinen:
¤
Die Folge wird in Teile zerlegt, die jeweils in den Hauptspeicher passen und daher getrennt voneinander mit internen Verfahren sorLert werden können.
Diese sorLerten Teilfolgen werden wieder in Dateien ausgelagert.
¤
Anschließend werden die Teilfolgen parallel einlesen und gemischt, indem jeweils das kleinste Element aller Teilfolgen gelesen und die neu Folge (d.h.
wieder eine Datei) geschrieben wird.
o
Der Merge Sort ist in der Praxis eine Mischung aus internem und externem
SorLeren. Für kleine Datenmengen kann man den Merge Sort als rein internes
Verfahren (mit einem Array) realisieren.
39
Gruppenarbeit
o Aufgabe: SorLeren von Spielkarten (z.B. Skatspiel – französisches Blan), wobei Sie nicht alle Spielkarten gleichzeiLg zugreifen (sehen) können. Acht Karten reichen aus.
o Ziel: Formulierung eines Algorithmus in Pseudocode zum SorLeren einer Folge von Spielkarten der Länge n.
o Zu beachten:
¤ Legen Sie fest wodurch ist die SorLerreihenfolge gegeben ist.
¤ Die Spielkarten liegen alle auf einem Stapel der auf 2 Stapel verteilt werden kann.
¤ Für das SorLeren liegen die Spielkarten jetzt auf 2 Stapeln.
¤ Sie können immer nur die oberste Karte des Stapels sehen.
¤ Die Spielkarten können wieder auf einen (drinen) Stapel gelegt werden.
o Denken Sie daran, dass immer eindeuLge Einzelschrine ausgeführt werden können, die präzise beschrieben werden müssen.
40
Merge Sort: Algorithmus (grob)
1. Zerlege die Folge F in zwei HälDen F
1und F
2.
2. Mische F
1und F
2durch KombinaLon einzelner Elemente zu geordneten Paaren.
3. Bezeichne die gemischte Sequenz mit F und wiederhole die Schrine 1 und 2, wobei dieses Mal die geordneten Paare zu geordneten Quadrupeln
zusammengefasst werden.
4. Wiederhole die voranstehenden Schrine durch Mischen der Quadrupel zu Octupeln und fahre damit solange fort, indem jedes Mal die Längen der
gemischten Sequenzen verdoppelt werden, bis die ganze Sequenz geordnet ist.
41
Merge Sort: Algorithmus
MergeSort (F) // sogenanntes reines oder direktes Mischen Eingabe: zu sorLerende Folge F der Länge n (n ist 2-‐er Potenz)
tl := 1; // aktuelle Lauflänge while tl < n do
Schreibe von der Folge F jeweils einen Lauf alternierend auf F1 und F2, bis alle Läufe von F nach F1 und F2, kopiert sind.
F := leere Folge; // Setze F auf die AusgangsposiLon zurück – zum Überschreiben;
for 1. Lauf to letzter Lauf do
while Lauf der Länge tl in F1 oder F2 noch nicht abgearbeitet do En•erne das kleinere Anfangselement aus F1 bzw. F2;
Füge dieses Element an F an;
od
Füge den verbliebenen nichtleeren Rest des aktuellen Laufs von F1 oder F2 an F an;
od
tl := tl * 2; // verdopple Lauflänge für nächsten Durchlauf od
42
Merge Sort: Beispiel
44 55 12 42 94 18 6 67
44 12 94 6
55 42 18 67
(44 55) (12 42) (18 94) (6 67) (44 55) (18 94)
(12 42) (6 67)
(12 42 44 55) (6 18 67 94)
(12 42 44 55) (6 18 67 94)
(6 12 18 42 44 55 67 94)
Merge
Split
Split Merge
Merge
Split
Run / Lauf
43
Merge Sort: EigenschaDen
o
Da eine Folge der Länge N in 2 Teilfolgen zerlegt wird und dann wieder zusammengefügt wird, nennt man das Verfahren auch 2-‐Wege-‐Mergesort.
o
Beginnt man mit Teilfolgen der Länge 1 und fährt solange in 2-‐er-‐Potenzen fort, bis man Teilfolgen der Länge N/2 zu einer (Ergebnis-‐) Folge der Länge N
zusammengefügt hat (wie im Algorithmus angegeben), dann nennt man das Verfahren reines 2-‐Wege-‐Mergesort (straight 2-‐way merge sort).
o
Der reine 2-‐Wege Mergesort ist ineffizient, da er immer mit einelemenLgen Teilfolgen beginnt und nicht eine VorsorLerung nutzt und auf möglichst langen vorsorLerten Teilfolgen au‚aut. Ein Verfahren, das dies umsetzt, nennt man natürlichen 2-‐Wege Mergesort.
o
Weitere Mergesort-‐Varianten sind:
¤
Mehr-‐Wege-‐Mergesort
¤
Mehrphasen-‐Mergesort
44
Natürlicher Merge Sort: Algorithmus
NaturalMergeSort (F) // sogenanntes natürliches Mischen Eingabe: zu sorLerende Folge F der Länge n
while Anzahl der Läufe auf dem Zielband F > 1 do
Teile Folge F in 2 Folgen F1 und F2 mit jeweils maximalen Lauflängen auf;
F := leere Folge; // Setze F auf die AusgangsposiLon zurück – zum Überschreiben;
for 1. Lauf to letzter Lauf do // Solange noch eine der beiden Folgen einen Lauf enthält while Lauf in F1 oder F2 noch nicht abgearbeitet do
En•erne das kleinere Anfangselement aus F1 bzw. F2; Füge dieses Element an F an;
od
Füge den verbliebenen nichtleeren Rest des aktuellen Laufs von F1 oder F2 an F an;
od od
45
Natürlicher Merge Sort: Beispiel
17 31 5 59 13 41 43 67 11 23 29 47 3 7 71 2 19 57 37 61 (17 31) (13 41 43 67) (3 7 71) (37 61)
(5 59) (11 23 29 47) (2 19 57)
(5 17 31 59) (11 13 23 29 41 43 47 67) (2 3 7 19 57 71) (37 61) (5 17 31 59) (2 3 7 19 57 71)
(11 13 23 29 41 43 47 67) (37 61)
(5 11 13 17 23 29 31 41 43 47 59 67) (2 3 7 19 37 57 61 71) (5 11 13 17 23 29 31 41 43 47 59 67)
(2 3 7 19 37 57 61 71)
(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 57 59 61 67 71)
Merge
Split
Split Merge
Merge
Split
46
Natürlicher Merge Sort: 2. Beispiel
3 2 5 11 7 13 19 17 23 31 29 37 43 41 47 59 57 61 71 67 (3) (7 13 19) (29 37 43) (57 61 71)
(2 5 11) (17 23 31) (41 47 59) (67)
(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 57 59 61 67 71)
Merge
Split
47
Weitere SorLerverfahren
o
Es gibt ein ganze Reihe weiterer SorLerverfahren (z.B.):
¤
Radixsort -‐ mit Fachverteilung,
¤
Quicksort,
¤
SorLeren mit Bäumen -‐ Heapsort
¤
und eine Reihe von VariaLonen dieser und der vorgestellten Verfahren.
o
Welches Verfahren letztendlich für ein Problem eingesetzt wird, ist abhängig von
der konkreten Aufgabenstellung, den Randbedingungen und der Effizienz des
jeweiligen Verfahrens.
48
QuickSort -‐ Grundidee
o
Zerlegung der zu sorLerenden Folge in 2 Teile bzw. Teilfolgen.
o
Alle Elemente der einen Folge sind kleiner als ein Referenzelement – das sog.
Pivot-‐Element – und alle Elemente der anderen Folge sind größer als das Referenzelement .
o
Das Pivot-‐Element kann beliebig gewählt werden (das erste, das letzte, das minlere).
o
Für die Teilfolgen wird dasselbe Prinzip angewendet (→ Rekursion).
o
Das Verfahren endet dann (Rekursionsende), wenn die zu sorLerende Folge die
Länge 0 hat.
49
QuickSort – Algorithmus Variante 1 (1)
QuickSort (F, u, o)
Eingabe: zu sorLerende Folge F, untere und obere Grenze u,o if o > u then
i := Zerlege (F, u, o);
QuickSort (F, u, i) QuickSort (F, i+1, o) fi
50
QuickSort – Algorithmus Variante 1 (2)
Zerlege (F, u, o) returns integer // Ordnet F bzgl. des minleren Elements Eingabe: zu sorLerende Folge F,
untere und obere Grenze u,o Ausgabe: PosiLon z der Zerlegung
p:= (u+o)/2; // Index des minleren Elements (Pivotelement) piv := F[p]; // Pivotelement
while u <= o do
l := Index des ersten Elements ab PosiLon u mit F[l] >= piv;
r := Index des letzten Elements unterhalb PosiLon o (inkl. o) mit F[r] <= piv;
if l < r then // sollten sich l und r überschninen haben, ist die Anordnung bereits richLg Tausche F[l] und F[r];
u := l+1;
o := r-‐1;
else return r; // schon ferLg, wenn l und r sich überschninen haben fi
od
return o;
51
QuickSort – Beispiel (mit Variante 1)
44 6 55 30 94 18
Ursprüngliche Schlüssel
44 6 18 30 94 55
1. Rekursionsstufe (2 mal)
6 44 18 30 55 94
2. Rekursionsstufe
6 18 44 30 55 94
3. Rekursionsstufe
6 18 30 44 55 94
Sortiertes Feld
53
QuickSort – Algorithmus Variante 2 (1)
QuickSort (F, u, o)
Eingabe: zu sorLerende Folge F,
untere und obere Grenze u, o if o > u then
i := Zerlege (F, u, o);
QuickSort (F, u, i-‐1) QuickSort (F, i+1, o) fi
Pivot- Position wird
ausgelassen
Idee:
Das Pivot-Element wird an die „richtige“ Stelle gesetzt. D.h. links, vom Pivot-Element stehen alle Werte <= Pivot-Element und rechts, alle Werte > Pivot-Element.
Das funktioniert nur dann, wenn man beispielsweise das am weitesten rechts stehend Element als Pivot-Element wählt. Dann vertauscht man, ähnlich dem 1.
Algorithmus, solange bis sich die beiden Zeiger überschnitten haben. Danach, wird das Pivot-Element an die richtige Position getauscht.
54
QuickSort – Algorithmus Variante 2 (2)
Zerlege (F, u, o) returns integer // Ordnet F bzgl. des am weitesten rechts stehenden Elements Eingabe: zu sorLerende Folge F,
untere und obere Grenze u, o Ausgabe: PosiLon z der Zerlegung
p:= o; // Index des am weitesten rechts stehenden Elements (Pivotelement) piv := F[p]; // Pivotelement
while u <= o do
l := Index des ersten Elements ab PosiLon u mit F[l] > piv; // wenn kein Element > piv, dann l = p
r := Index des letzten Elements unterhalb PosiLon o mit F[r] <= piv; // wenn kein Element <= piv, dann r = u-‐1 if l < r then // sollten sich l und r überschninen haben, ist die Anordnung bereits richLg
Tausche F[l] und F[r];
u := l+1;
o := r-‐1;
else
Tausche F[l] und F[p];
return l; // schon ferLg, wenn l und r sich überschninen haben fi
od
Tausche F[u] und F[p]; // Pivotelement an die richLge Stelle bringen return u;
55
QuickSort – Algorithmus Variante 3 (1)
QuickSort (F, u, o)
Eingabe: zu sorLerende Folge F,
untere und obere Grenze u, o if o > u then
i := Zerlege (F, u, o);
QuickSort (F, u, i-‐1) QuickSort (F, i+1, o) fi
Idee:
Wie bei der 2. Variante wird das Pivot-Element wird an die „richtige“ Stelle gesetzt.
Anders ist hier nur Art des Tausches von Elementen.
Man läuft mit 2 versetzen Zeigern von links nach rechts und tauscht über diese Zeiger referenzierte Elemente, wenn der vordere Zeiger auf ein Element kleiner als das Pivot-Element trifft. Am Ende wird das Pivot-Element an die richtige Position getauscht.
Pivot- Position wird
ausgelassen
56
QuickSort – Algorithmus Variante 3 (2)
Zerlege (F, u, o) returns integer // Ordnet F bzgl. des am weitesten rechts stehenden Elements Eingabe: zu sorLerende Folge F,
untere und obere Grenze u, o Ausgabe: PosiLon z der Zerlegung
p := o; // Index des am weitesten rechts stehenden Elements (Pivotelement) index := u; // index und zeiger laufen jeweils von links nach rechts
for zeiger := u to o-‐1 do
if F[zeiger] <= F[p] then
Tausche(F[index], F[zeiger]);
index := index + 1;
fi od
Tausche(F[index], F[p]); // Pivotelement an die richLge Stelle bringen return index;
58
QuickSort -‐ Hinweise
Zur Programmierung:
o
QuickSort ist sehr empfindlich gegen minimale Programmänderungen. Jede Version einer ImplemenLerung muss sorgfälLg darauƒin überprüD werden, ob sie auch wirklich in allen Fällen das korrekte Ergebnis liefert.
[Onmann,Widmayer]
Verschiedene QuickSort-‐Varianten unterscheiden sich:
o
In der Erminlung der Zerlegung.
o
In der Berechnung des Pivot-‐Elements.
59
Merge Sort – Variante für internes SorLeren
Idee: Übertragung des Verfahren für externes Mischen auf Mischen mit einem Array.
Vorgehen ähnlich wie beim QuickSort.
Vorgehensweise:
1. Zu sorLerende Folge wird in 2 Teilfolgen zerlegt.
2. Wenn diese beiden Teilfolgen sorLert sind, dann mischt man sie zur sorLerten Gesam•olge.
3. Die Teilfolgen werden rekursiv solange weiter zerlegt, bis man eine sorLerte Teilfolge erhält. Das ist dann, der Fall, wenn man bei Teilfolgen angekommen ist, die nur noch aus einem Element bestehen.
4. Beim Abarbeiten der Rekursion mischt man dann immer längere Teilfolgen, die beim rekursiven Aufsplinen entstanden sind, zusammen
5. Am Ende erhält man die sorLerte Gesam•olge.
60
Merge Sort – intern 1/2
Merge (F1,F2) returns Folge // mischt die Folgen F1 und F2 in die neue Folge F Eingabe: zwei zu sorLerende Folgen F1 und F2
Ausgabe: sorLerte Folge F
F := leere Folge;
while F1 oder F2 noch nicht abgearbeitet do
En•erne das kleinere Anfangselement aus F1 bzw. F2; Füge dieses Element an F an;
od
Füge den verbliebenen nichtleeren Rest von F1 oder F2 an F an;
return F
61
Merge Sort – intern 2/2
MergeSort (F) returns Folge // internes Mischen eines Arrays Eingabe: zu sorLerende Folge F
Ausgabe: sorLerte Folge FS
if F einelemenLg then // Rekursionsende return F
else
teile F in F1 und F2 // beim internen SorLeren erfolgt das Teilen durch Index-‐Bereiche
F1 = MergeSort (F1)
F2 = MergeSort (F2) return Merge (F1, F2) fi
62
4. Kapitel Teil 2:
SorMerverfahren im Vergleich
63
SorLerverfahren im Vergleich
o Aufwandsabschätzungen werden später noch im Detail behandelt.
o Aber vorab:
¤
WER würde welches Verfahren (wozu) einsetzen?
¤
Gruppenarbeit: Algorithmen analysieren und bewerten !
¤
Es soll nur der Laufzeitaufwand betrachtet werden.
o Einige Vorbemerkungen:
¤
Der Laufzeitaufwand ist i.w. durch die Anzahl der Schlüsselvergleiche und die Anzahl der Vertauschungen besLmmt.
¤
Der Aufwand muss nicht ganz genau erminelt werden. Es reicht schon aus, den Aufwand näherungsweise, d.h. in einer besLmmten Größenordnung zu besLmmen. So ist es sicherlich unbedeutend, wenn man beispielsweise n -‐1 durch n ersetzt. Bei sehr großen Werten von n spielt ein um 1
kleinerer Wert ganz sicher keine Rolle.
¤
Bei einer endgülLgen Bewertung der Verfahren spielen Faktoren wie die
Größe oder die Beschaffenheit des zu sorLerenden Feldes eine wichLge
Rolle.
64
SelecLon Sort: Algorithmus
SelecMonSort (F)
Eingabe: zu sorLerende Folge F der Länge n p := n;
while p > 1 do
g := Index des größten Elementes aus F im Bereich 1 – p;
Vertausche Werte von F[p] und F[g] wenn p ! = g;
p := p-‐1;
od
65
SelecLonSort: EigenschaDen
o Man beginnt mit dem letzten Element als aktuellem Element und geht bei jedem Durchlauf um 1 nach links. Bei jedem Durchlauf wird das aktuelle Element mit dem größten Element links davon vertauscht.
o Die Anzahl der Vergleiche ist also unabhängig vom Aussehen der Eingabereihenfolge, egal ob die Folge schon sorLert ist oder in umgekehrter Reihenfolge sorLert ist. Für die Anzahl der Vergleiche ergibt sich:
(n-‐1) + (n-‐2) + (n-‐3) + . . . + 2 + 1 = n (n – 1)/2 ≈ n2/2
o Vertauscht wird dann, wenn ein größeres Element gefunden wird. Ist die Folge schon sorLert (günsLgster Fall), dann gibt es keine Vertauschungen, steht das kleinste Element hinten und ist der Rest sorLert (schlechtester Fall) ist sie = n -‐ 1. Im Minel deshalb = n/2.
o Das ergibt in der Summe für Vergleiche und Vertauschungen im MiQel:
≈ n
2/2 + n/2
o Das Verfahren ist instabil, wegen der Vertauschungen, dadurch werden relaLve Reihenfolgen nicht beibehalten.
66
InserLon Sort: Algorithmus
InserMonSort (F)
Eingabe: zu sorLerende Folge F der Länge n
for i := 2 to n do // Element an PosiLon i wird an der passenden Stelle eingefügt m := F[i]; // merke einzufügendes Element
j := i;
while j > 1 do // laufe von rechts nach links
if F[j-‐1] >= m then // verschiebe F[j-‐1] eine PosiLon nach rechts
F [j] := F[j-‐1];
j := j-‐1;
else
Verlasse innere Schleife (= while-‐Schleife) // weil EinfügeposiLon gefunden fi;
od; // while-‐Schleife
F [j] :=m; // gemerktes Element an der richLgen PosiLon einfügen od; // for-‐Schleife
67
InserLonSort: EigenschaDen (1)
o Man beginnt mit dem 2. Element als aktuellem Element und prüD, wo es links davon eingefügt werden muss. Man fährt dann mit dem 3. Element fort. Solange, bis man beim letzten Element angekommen ist. Muss ein Element eingefügt werden, dann werden alle anderen ab der EinfügeposiLon bis zur PosiLon des aktuellen Elements um eine PosiLon nach rechts verschoben.
o Die Anzahl der Vergleiche ist abhängig von der Art der Eingabereihenfolge: ist die Folge in umgekehrter Reihenfolge sorLert (der schlechteste Fall bzgl. der Vergleiche), dann werden alle Elemente miteinander verglichen:
1 + 2 + . . . + (n-‐3) + (n-‐2) + (n-‐1) = n (n – 1)/2 ≈ n
2/2
(schlechtester Fall)o Sind die Elemente schon sorLert (der günsMgste Fall bzgl. der Vergleiche), dann ist man nach dem jeweils ersten Vergleich schon ferLg:
1 + 1 + . . . + 1 = n – 1
(bester Fall)o In der Summe:
(n-‐1) + 1 + 2 + . . . + (n-‐3) + (n-‐2) + (n-‐1) = n (n + 1)/2 – 1 ≈ n
2/2
o Im Minel:
≈ n
2/4
68
InserLonSort: EigenschaDen (2)
o Für die Verschiebungen verhält es sich ganz ähnlich:
o Sind die Elemente schon sorLert (günsLgster Fall) , dann ist keine Verschiebung notwendig.
o Sind die Elemente in umgekehrter Reihenfolge (schlechtester Fall) sorLert, dann sind 1 + 2 + . . . + (n-‐3) + (n-‐2) + (n-‐1) = n
⋅
(n – 1)/2 ≈ n2/2 Verschiebungen notwendigo Im Minel: ≈ n2/4.
o Das ergibt in der Summe für Vergleiche und Verschiebungen im MiQel:
≈ 2 ⋅ n
2/4
o Das Verfahren ist stabil, weil bei den Verschiebungen relaLve Reihenfolgen nicht verändert werden.
69
Bubble Sort -‐ Algorithmus
BubbleSort (F)
Eingabe: zu sorLerende Folge F der Länge n (n > 1) j := 1;
do
for i := 1 to n -‐ j do if F[i] > F[i+1] then
Vertausche Werte von F[i] und F[i+1];
fi;
od;
j := j +1;
while Vertauschungen stan gefunden haben und j <= n;
70
BubbleSort: EigenschaDen
o Bei jedem Durchlauf werden alle Elemente paarweise miteinander ver-‐glichen und ggf.
miteinander vertauscht. Dabei wird das jeweils größte Element gefunden und durch Vertauschen mit seinem rechten Nachbarn an das Ende der Folge bewegt.
o Die Anzahl der Vergleiche und Vertauschungen ist abhängig von der An-‐ordnung der
Eingabereihenfolge: ist die Folge in umgekehrter Reihenfolge sorLert (der schlechteste Fall), dann werden im i-‐ten Durchlauf n-‐i Elemente miteinander verglichen und vertauscht:
2 ⋅ ((n-‐1) + (n-‐2) + (n-‐3) + . . . + 2 + 1) = n (n – 1) ≈ n
2o Ist die Folge schon sorLert (der günsMgste Fall), ist nur ein Durchlauf notwendig:
n-‐1 Vergleiche und keine Vertauschungen
o In der Summe:
(n-‐1) + 2 ((n-‐1) + (n-‐2) + (n-‐3) + . . . + 2 + 1) = n (n + 1) – 1 ≈ n2
o Im Minel:
≈ n
2/2
o Zwei benachbarte Elemente werden nur dann getauscht, wenn das erste Element größer ist, d.h. das Verfahren ist stabil.
71
Merge Sort: Algorithmus
MergeSort (F) // sogenanntes reines oder direktes Mischen Eingabe: zu sorLerende Folge F der Länge n (n ist 2-‐er Potenz)
tl := 1; // aktuelle Lauflänge while tl < n do
Teile Folge F in der Mine in 2 gleich lange Folgen F1 und F2 auf;
F := leere Folge; // Setze F auf die AusgangsposiLon zurück – zum Überschreiben;
for 1. Lauf to letzter Lauf do
while Lauf der Länge tl in F1 oder F2 noch nicht abgearbeitet do En•erne das kleinere Anfangselement aus F1 bzw. F2;
Füge dieses Element an F an;
od
Füge den verbliebenen nichtleeren Rest des aktuellen Laufs von F1 oder F2 an F an;
od
tl := tl * 2; // verdopple Lauflänge für nächsten Durchlauf od
72
MergeSort: EigenschaDen (1)
o Die Anzahl der Durchläufe ist (beim reinen MergeSort) konstant. Die Anzahl der OperaLonen pro Durchlauf auch.
o Beim reinen MergeSort wird die Lauflänge (beginnend mit 1) bei jedem Durchlauf
verdoppelt. D.h. dass man bei n Elementen (n ist eine 2-‐er Potenz! ) log2n Durchläufe erhält.
o Bei jedem Durchlauf werden alle n Elemente des Ausgangsbands gleichmäßig auf die beiden Hilfsbänder verteilt: 2 ⋅ n / 2 = n KopieroperaLonen. Da es log n Durchläufe gibt, ist die
Gesamtzahl der KopieroperaLonen beim Zerlegen = n
⋅
log2n.o Beim Zusammenfügen der Läufe auf den Hilfsbändern werden
pro Durchlauf (Lauflänge-‐1) ⋅ n / Lauflänge Vergleiche gemacht (beachte: die Lauflänge ist immer eine 2-‐er Potenz), um über die Reihenfolge auf dem Ausgangsband zu entscheiden.
Für die Gesamtzahl SV der Vergleiche ergibt sich demnach:
SV = (1 ⋅ n / 2) + (3 ⋅ n / 4) + (7 ⋅ n / 8) + . . . + ((2
log n-‐1) ⋅ n / 2
log n) = (1 ⋅ n / 2) + (3 ⋅ n / 4) + (7 ⋅ n / 8) + . . . + ((n-‐1) ⋅ n / n)
= (1/2 ⋅ n ) + (3/4 ⋅ n) + (7/8 ⋅ n) + . . . + (n-‐1)
73
MergeSort: EigenschaDen (2)
o Jeder einzelne Summand ist kleiner als n, die Anzahl der Summanden ist log n. Deshalb gilt die für die Summe (SV) folgende Abschätzung:
SV < n ⋅ log n
o Beim Zurückschreiben auf das Ausgangsband gibt es genau n KopieroperaLonen. Da es log n Durchläufe gibt, ist die Gesamtzahl der KopieroperaLonen beim Zurückschreiben = n
⋅
log n.o Insgesamt gilt für den Gesamtaufwand (GA):
GA <= Anzahl Durchläufe
⋅
(KopieroperaLonen beim AuDeilen + VergleichsoperaLonen + KopieroperaLonen beim Mischen )
≈ log n ⋅ (n + n + n) ≈ n ⋅ log n
o Die relaLve Ordnung der Elemente bleibt erhalten, da immer nur verschoben und nicht getauscht wird. Das Verfahren ist also stabil.
o Der natürliche MergeSort hat auf jeden Fall ein besseres Laufzeitver-‐halten, da er höchstens genauso viele Läufe hat wie der reine MergeSort.
74
QuickSort – Algorithmus 1. Variante 1/2
QuickSort (F, u, o)
Eingabe: zu sorLerende Folge F, untere und obere Grenze u,o if o > u then
i := Zerlege (F, u, o);
QuickSort (F, u, i) QuickSort (F, i+1, o) fi
75
QuickSort – Algorithmus 1. Variante 2/2
Zerlege (F, u, o) // Ordnet F bzgl. des minleren Elements Eingabe: zu sorLerende Folge F,
untere und obere Grenze u,o Ausgabe: PosiLon z der Zerlegung
p:= (u+o)/2; // Index des minleren Elements (Pivotelement) piv := F[p]; // Pivotelement
while u <= o do
l := Index des ersten Elements ab PosiLon u mit F[l] >= piv;
r := Index des letzten Elements unterhalb PosiLon o (inkl. o) mit F[r] <= piv;
if l < r then // sollten sich l und r überschninen haben, ist die Anordnung bereits richLg Tausche F[l] und F[r];
u := l+1;
o := r-‐1;
else return r; // schon ferLg, wenn l und r sich überschninen haben fi
od
return o;