Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann...

53
Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät für Angewandte Wissenschaften Albert-Ludwigs-Universität Freiburg Sortierverfahren

Transcript of Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann...

Page 1: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II, SS 2008Algorithmen und Datenstrukturen

Vorlesung 10Prof. Dr. Thomas Ottmann

Algorithmen & Datenstrukturen, Institut für InformatikFakultät für Angewandte WissenschaftenAlbert-Ludwigs-Universität Freiburg

Sortierverfahren

Page 2: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 2

Gegeben sind n Objekte, die über einen (ganzzahligen oder alphabetischen) Schlüssel identifiziert werden und im Hauptspeicher in einem Array abgelegt sind (interne Sortierverfahren).

Aufgabe:

Die Objekte sollen so umgeordnet werden, dass die Schlüssel aufsteigend sortiert sind.

Vereinfachende Annahme:

Die zu sortierenden Daten sind Buchstaben oder ganze Zahlen

Sortieren: Aufgabenbeschreibung

Page 3: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 3

Rahmen für Sortier-Verfahren

• Für die Darstellung der Verfahren wird im Folgenden der Einfachheit halber ein Array

von Zeichen (char) sortiert.

• Wir verwenden ein simples Rahmen-Programm.

class CharSort {

public static void main (String args[]) {

char [] a = "thequickbrownfoxjumpsoverthelazydog".toCharArray();

System.out.println ("Zeichen-Sortierprogramme\n");

System.out.println (a);

System.out.print ("bubbleSort: ");

Sorter.bubbleSort (a);

System.out.println (a);

} // main

} // CharSort

Page 4: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 4

Die Sorter-Klasse

Die Sorter-Klasse vereint verschiedene Sortier-Algorithmen sowie Hilfs-

Methoden:

class Sorter {

private final static void swap (char[] a, int i, int k){

char h = a[i];

a[i] = a[k];

a[k] = h;

} // swap

// verschiedene Sortier-Verfahren ...

} // Sorter

Die Ausgabe eines Sortier-Laufs sieht dann etwa so aus:

C:\algo>java CharSort

Zeichen-Sortierprogramme

thequickbrownfoxjumpsoverthelazydog

bubbleSort: abcdeeefghhijklmnoooopqrrsttuuvwxyz

Page 5: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 5

(1) Allgemeine Sortierverfahren:

Zur Ermittlung der korrekten Anordnung sind nur Vergleichsoperationen zwischen Schlüsseln erlaubt, die richtige Reihenfolge wird durch Bewegungen der Schlüssel hergestellt.

Bubblesort,

Insertion Sort,

Selection Sort,

Heapsort,

Quicksort,

Mergesort,

(2) Spezielle Sortierverfahren:

Distribution Sort: Sortieren durch Fachverteilung,

Radixsort

Sortieren: Zahlreiche Lösungen

Page 6: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 6

Untere Komplexitätsschranke für Sortierverfahren

Satz. Jeder Sortier-Algorithmus für n Elemente, der ausschließlich auf

Schlüssel-Vergleichen und Element-Vertauschungen beruht, hat eine worst-

case Laufzeit Ω(n log n).

• Am Ende des Sortier-Vorgangs muss eine von n! Permutationen der n

Elemente erzeugt worden sein.

• Jeder Ausgang eines Vergleiches liefert Informationen um die in Frage

kommende Menge von Permutationen in zwei Teile zu zerlegen.

Page 7: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 7

Kriterien zur Beurteilung verschiedener Verfahren

Laufzeit:

Anzahl ausgeführter Schlüsselvergleiche,

Anzahl ausgeführter Bewegungen von Schlüsseln

(bester, schlechtester, mittlerer Fall)

Weitere Kriterien:

Speicherbedarf (in situ?)

Berücksichtigung von Vorsortierung

Stabilität

Page 8: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 8

Bubble Sort

Wiederholung: Das Blasen-Aufstiegs-Verfahren

static void bubbleSort (char[] a){

int hi = a.length-1;

for (int k = hi; k > 0; k--){

boolean test = true;

for (int i = 0; i < k; i++)

if (a[i] > a[i+1]) {

swap (a, i, i+1);

test = false;

}

if (test) break;

}

} // bubbleSort

• Beginnend mit dem höchsten Index wird das Array sortiert.

• Vorsortierung wird ausgenutzt

• Laufzeiten in O(n²) im worst und average case sowie Ο(n) im best case.

Page 9: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 9

Bubblesort: Beispiel

Page 10: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 10

Selection Sort (1)

• Suche das kleinste der verbleibenden Elemente und hänge es an den bisher

sortierten Bereich (durch Tausch) an.

• Am Ende ist alles sortiert.

static void selectionSort (char[] a){

int hi = a.length-1;

for (int k = 0; k < hi; k++){

int min = minPos (a, k, hi); // finde minPos im Rest

if (min != k) swap (a, min, k); // tausche es nach k

}

} // selectionSort

private static int minPos (char[] a, int lo, int hi){

int min = lo;

for (int i = lo+1; i <= hi; i++)

if (a[i] < a[min]) min = i;

return min;

} // minPos

Page 11: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 11

Selection Sort: Beispiel

Page 12: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 12

Selection Sort (2)

• Äußere Schleifendurchläufe: (n - 1)

• Bestimmung von minPos (a,k,hi): (n - k) Vergleiche.

• Gesamtaufwand: O(n²)

• Vorteil: Nur (n - 1) Vertauschungen, besser als bei Bubble Sort.

• Nachteil: Bei Vorsortierung keine Verbesserung.

Page 13: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 13

Insertion Sort (1)

• Das erste Element des noch unsortierten Bereiches wird genommen und in den

schon sortierten an der richtigen Stelle (durch Tausch) eingefügt.

• Die bereits sortierten Elemente oberhalb der Einfügestelle müssen dazu je um

eine Position verschoben werden.

static void insertionSort (char[] a){

int hi = a.length-1;

for (int k = 1; k <= hi; k++)

if (a[k] < a[k-1]){

char x = a[k];

int i;

for ( i = k; ( (i > 0) && (a[i-1] > x) ) ; i-- )

a[i] = a[i-1]; // rechts schieben

a[i] = x; // einfuegen

}

} // insertionSort

Page 14: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 14

Insertion Sort: Beispiel

Page 15: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 15

Insertion Sort (2)

• Aufwand wieder in O(n²) im Mittel und im worst case.

• Es werden viele Vertauschungen ausgeführt.

• Vorsortierung ist von Vorteil.

Page 16: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 16

Heapsort: Idee

Effizientes Verfahren zum Sortieren (auch im worst case in O(n log n))

Prinzip: Sortieren durch wiederholtes Auswählen des Maximums (ähnlich Selection Sort)

Besonderheit: Verwendung einer Datenstruktur Heap, welche die Bestimmung des Maximums effizient unterstützt

Definition: Eine Folge F = (k1, k2, . . . , kn) von Schlüsseln ist ein (Max-) Heap,

wenn für alle i {1, 2, …, n/2} gilt: ki ≥ k2i und ki ≥ k2i+1.

Bsp:

1 2 3 4 5 6 7

7 5 6 4 2 1 3

7 6 5 2 3 4 1

Page 17: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 17

Heap-Bedingung

Page 18: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 18

Veranschaulichung von Heaps

Veranschaulichung des Heaps durch Binär-Baum mit Positionsnummern:

Level i hat 2i Knoten (außer u.U. dem letzten)

Knoten sind von oben nach unten und von links nach rechts nummeriert.

Knoten i hat Knoten 2i als linken und Knoten 2i + 1 als rechten Nachfolger und Knoten i/2 als Vorgänger (falls jeweils vorhanden).

Aus der Heap-Bedingung ki ≥ k2i und ki ≥ k2i+1 folgt:

Das Maximum steht an der Wurzel (an Index 1) und kann sofort entfernt werden.

Doch wie erhält man wieder einen Heap für den Rest?

1 2 3 4 5 6 7

7 6 5 2 3 4 1

7

6 5

2 3 4 1

1

2 3

54 6 7

Page 19: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 19

Heapsort: 2 Phasen

Sortieren mit Hilfe eines Heaps:

Phase 1: Verwandeln des gegebenen Arrays in einen Heap

Phase 2: Iterieretes Extrahieren des Maximums (von Position 1), Tauschen des Elementes mit höchstem Index im nichtsortierten Teil an Position 1 und versickern lassen im Rest.

Phase 2 wird n-Mal ausgeführt, jede Ausführung kostet höchstens O(log n)

Schlüsselvergleiche.

Wir werden zeigen:

Phase 1 kann in Zeit O(n) ausgeführt werden.

Gesamtlaufzeit von Heapsort: O(n log n)

Page 20: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 20

Heapsort-Beispiel: Erster Schritt

Extrahieren des größten Elementes (an Position 1) und tauschen an das rechte Ende des Arrays (höchster Index im nichtsortierten Teil).

7

6 5

2 3 4 1

1

2 3

54 6 7

1 2 3 4 5 6 7

7 6 5 2 3 4 1

Page 21: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 21

Heapsort-Beispiel: Weitere Schritte

Das Element von der größten verbleibenden Index-Position wird nach Pos. 1

bewegt und dann versickert. Sobald wieder ein Heap entstanden ist, kann das

nächste Maximum entfernt werden, bis der Heap leer ist.

1

4 5

3

6

2

1

4 5

3

6

2

1

3 42

6 5

6

1 42

3 5

1

4 5

3

6

2

6

3 42

1 5

1

4 5

32

1

4 5

32

4

12

3 5

5

12

3 4

1

4

32

1

4

2

3

1

4

32 1

4

2

3

1

32

2

13

1

32

3

12

4

6 5

Page 22: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 22

Heapsort: Versickern von Elementen

Versickern bedeutet: Vertauschen mit dem größeren der Nachfolger, solange dieser größer

als das zu versickernde Element ist.

Verfahren zum Versickern des ersten Elements in einem Array a in den Grenzen j und t:

private static void percolate (char[] a, int j, int t){

int h;

while ((h=2*j) <= t){ // h: linker Nachfolger

if (h < t && a[h+1] > a[h]) h++; // h: größerer

// Nachfolger

if (a[h] > a[j]) { // versickern nötig?

swap (a, h, j); // ja: vertauschen

j = h; // und weiter

}

else break; // nein: fertig

}

} // percolate

Anzahl der Vergleiche und Vertauschungen, um ein Element in n Elementen zu

versickern: O(log n)

Page 23: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 23

Sortieren mit Hilfe eines Heaps:

Erst muss ein Heap erstellt werden.

Dann kann man immer wieder das jeweilige Maximum entnehmen.

Da bei n Elementen diejenigen mit den Positionen n/2 + 1, …, n bereits die Heap-Bedingung erfüllen, kann man bei der Heap-Erstellung mit dem Versickern beim Element mit der Nr. n/2 beginnen.

static void heapSort (char[] a){

int j, hi = a.length-1;

for (j = hi/2; j >= 1;) // alle Pos. j = [n/2] .. 1:

percolate (a, j--, hi); // versickere im Array j ... N

for (j = hi ; j > 1;) { // alle Pos. j = n ... 2:

swap (a, 1, j); // vertausche Maximum nach j

percolate (a, 1, --j); // versickere im Array 1 ... (j-1)

}

} // heapSort

Frage: Wie aufwendig ist der Heap-Aufbau durch iteriertes Versickern?

Heap-Aufbau: Implementation

Page 24: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 24

Heapaufbau

Niveau, #Knoten, Versick.-Aufwand/Knoten

Page 25: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 25

Heapaufbau: Kosten

Heap-Aufbau durch iteriertes Versickern:0 20

1 21

2 2² k = 3 2k

Knoten

Anzahl der Knoten insgesamt: n = 2k+1 - 1

Anzahl der Vergleiche zum Aufbau:

Der Heap-Aufbau erfolgt also in linearer Zeit.

)()1(*2

)22(*2

2*2)(2*2

1

1

0 1

nkn

k

jik

k

k

i

k

j

jki

Page 26: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 26

Aufwand für Heapaufbau

Gesamtaufwand = 202k + 21 2(k-1) + 22 2(k-2) + … + 2(k-1) 2

= 2 S(k), mit S(k) = 20k + 21(k-1) + 22(k-2) + … + 2(k-1)1

= 2 (n – k – 1) = O(n)

Page 27: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 27

Heapsort: Mögliche Verbesserungen

Laufzeit für Heap Sort insgesamt: O(n log n).

Platzbedarf: n für das Array sowie zusätzlich: O(1).

Frage: Kann Heap Sort noch verbessert werden?

Beobachtungen:

Der konstante Faktor c des Verfahrens hängt von der Tiefe ab, um die versickert werden muss.

Im schlimmsten Fall wird immer das kleinste Element bis ganz unten versickert.

Auch die erwartete Tiefe beim Versickern ist hoch.

Pro Niveau, um das ein Element versickert wird, treten 2 Schlüsselvergleiche auf, um den größeren der Nachfolger zu bestimmen und um festzustellen, ob weiter versickert werden muss.

Verbesserungsidee:

Bestimme den größeren der Nachfolger eines Elementes mit einem Schlüsselvergleich.

Versickere auf jeden Fall (auch wenn das Element zu groß ist).

Unten angekommen, lasse das Element wieder aufsteigen, soweit nötig.

Page 28: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 28

Bottom-Up Heap Sort (1)

Bemerkung:

Die gefundene Stelle für das Element stimmt mit derjenigen der Versickerungsprozedur

überein.

Satz. Das verbesserte Verfahren Bottom-up Heapsort benötigt zum Sortieren einer

Folge von n Schlüsseln im schlechtesten Fall nur 1.5 n log n + 2n Schlüsselvergleiche.

Im Mittel benötigt Bottom-up Heapsort nur n log n + O(n) Schlüsselvergleiche.

static void bottomUpheapSort (char[] a){

int j, hi = a.length-1;

for (j = hi/2; j >= 1;)

percolateb (a, j--, hi); // versickere (mod) von j bis hi

for (j = hi ; j > 1;) {

swap (a, 1, j); // größeres nach j

percolateb (a, 1, --j); // versickere (mod)

}

} // bottomUpheapSort

Page 29: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 29

Bottom-Up Heap Sort (2)

private static void percolateb (char[] a, int j, int t){

int h;

while ((h=2*j) <= t){ // h: linker Nachfolger

if (h < t && a[h+1] > a[h]) h++; // h: größerer Nachfolger

swap (a, h, j); // tausche auf jeden Fall

j = h; // und weiter

}

bubbleUp (a, j); // tausche wieder zurück wenn nötig

} // percolateb

private static void bubbleUp (char[] a, int j){

char x = a[j]; // Position für x gesucht

for (; j > 1 && a[j/2] < x; j/=2) // bis nach oben, falls nötig:

a [j] = a [j/2]; // ziehe Elemente herunter

a[j] = x; // positioniere x

} // bubbleUp

Page 30: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 30

Quicksort (1): Sortieren durch Teilen

Divide-&-Conquer Prinzip

Sehr gute Laufzeit im Mittel, schlechte Laufzeit im worst case.

Quicksort Eingabe: unsortierte Liste L, Ausgabe: sortierte Liste

Quicksort(L):

if (|L| <= 1)

return L

else waehle Pivotelement p aus L

L< = {a in L | a < p}

L> = {a in L | a > p}

return

[Quicksort(L<)] + [p] + [Quicksort(L>)]

Lin

L<

p

p L>

Quicksort Quicksort

Page 31: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 31

Quicksort (2): Implementationclass QuickSort extends SortAlgorithm {

static void sort (Orderable A[]){/* sortiert das ganze Array */sort (A, 1, A.length-1);

}static void sort (Orderable A[], int l, int r){

/* sortiert das Array zwischen Grenzen l und r */if (r > l) { // mind. 2 Elemente in A[l..r]

int i = divide(A, l, r);sort (A, l, i-1);sort (A, i+1, r);

}}static int divide (Orderable A [], int l, int r) {

/* teilt das Array zwischen l und r mit Hilfedes Pivot-Elements in zwei Teile auf und gibtdie Position des Pivot-Elementes zurueck */...

}

}

Page 32: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 32

Quicksort (3): Der Aufteilungsschrittdivide(A; l; r):

- liefert den Index des Pivotelements in A

- ausführbar in Zeit O(r – l)

l r

Page 33: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 33

Quicksort (4): Implementation, Aufteilungsschritt

static int divide (Orderable A [], int l, int r) { // teilt das Array zwischen l und r mit Hilfe // des Pivot-Elements in zwei Teile auf und gibt // die Position des Pivot-Elementes zurueck

int i = l-1; // linker Zeiger auf Array

int j = r; // rechter Zeiger auf Array

Orderable pivot = A [r]; // das Pivot-Element while (true){ // "Endlos"-Schleife

do i++; while (i < j && A[i].less(pivot));do j--; while (i < j && A[j].greater(pivot));if (i >= j) {

swap (A, i, r);return i; // Abbruch der Schleife

}swap (A, i, j);

}

}

Page 34: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 34

Quicksort (5): Analyse

Günstigster Fall:

Schlechtester Fall:

log n

Tmin = O(n log n)

n

Tmax = O(n2)

Page 35: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 35

Quicksort (6): Wahl des Pivotelementes

Die Wahl von w (das Pivot-Element) ist entscheidend für die Effizienz: Sind Fl und Fr annähernd gleich groß, resultiert Laufzeit in O(n log n).

Eine gute Wahl von w wäre ein mittleres Element, allerdings aufwendig zubestimmen.

Ein einfacherer Ansatz ist die Wahl des (von der Größe her) mittleren von drei Elementen, die links, rechts und in der Mitte der Folge stehen. Dabei werden sie direkt schon sortiert, womit kleine Probleme (bis zur Größe 3) bereits gelöst sind.

Page 36: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 36

Quicksort (7): Medium-of-Three Variante

private static void quickSort (char[] a, int lo, int hi){

int li = lo;

int re = hi;

int mid = (li+re)/2;

if (a[li] > a[mid]) swap(a, li, mid); // Kleines Problem

if (a[mid] > a[re] ) swap(a, mid, re); // und Vorsortieren

if (a[li] > a[mid]) swap(a, li, mid); // per Bubble Sort

if ((re - li) > 2){ // Großes Problem:

char w = a[mid]; // Divide:

do{ while (a[li] < w) li++; // suche li

while (a[re] > w) re--; // suche re

if (li <= re) swap (a, li++, re--); // vertausche

} while (li <= re); // bis fertig

if (lo < re) quickSort (a, lo, re); // Conquer links

if (li < hi) quickSort (a, li, hi); // Conquer rechts

} // Merge unnötig

} // quickSort

Page 37: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 37

Erfolgt die Aufteilung der Folgen jeweils gleichmäßig (best case), so gilt:T(n) = 2 T(n/2) + O(n) und damit T(n) = O(n log n)

Im ungünstigsten Fall (worst case) liegt das Pivot-Element ganz links oder rechts:T(n) = T(1) + T(n - 2) + O(n) und dann T(n) = O(n2)

Mittelt man über sämtliche Permutationen von n Schlüsseln und nimmt an, dass die Wahl des Pivot-Elementes immer zufällig erfolgt (average case), so ergibt sich für die Anzahl von Schlüsselvergleichen:

)log(

1))()1((1

)(1

nn

nknTkTn

nTn

k

Quicksort (8): Laufzeit-Abschätzung

Page 38: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 38

Merge-Sort (1)

• Merge-Sort ist wie Quicksort ein Divide-&-Conquer Verfahren, anders als dieses aber mit simplem Divide- und komplizierterem Merge-Schritt.

• Die Datensätze der zu sortierenden Folge werden, wenn sie nicht klein genug zum direkten Sortieren sind, in zwei annähernd gleich große Teilfolgen aufgeteilt.

• Die Teilfolgen werden nach dem gleichen Verfahren sortiert.

• Anschließend werden die sortierten Teilfolgen zusammengemischt (merge).

• Beim Mischen müssen jeweils nur die ersten Elemente beider Folgen verglichen werden, das kleinere wird entfernt und kommt in die Ergebnisfolge.

• Ist eine der Teilfolgen ganz ausgeschöpft, müssen die restlichen Elemente der verbleibenden Teilfolge nur noch umkopiert werden.

• Eine Teilfolge mit nur einem Element ist bereits sortiert.

Page 39: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 39

Merge-Sort (2)

• Der nicht-rekursive Einstieg in Merge-Sort:

static void mergeSort(char[] a){

int hi = a.length-1;

mergeSort (a, 0, hi); // die rekursive Variante

} // mergeSort

• Das Zusammen-Mischen der Teilfolgen kann nicht im gleichen Array erfolgen.

• Es wird ein Hilfs-Array in der Größe der Ergebnisfolge benötigt.

Page 40: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 40

Merge-Sort (3): Implementation

private static void mergeSort (char[] a, int lo, int hi){

if (lo < hi) { // Grosses Problem:

int m = (lo + hi +1)/2; // Divide

mergeSort (a, lo, m-1); // Conquer links

mergeSort (a, m, hi); // Conquer rechts

// Merge nach

char [] temp = new char[hi-lo+1]; // Hilfs-Array

for (int i=0, j=lo, k=m; i<temp.length;) // bis voll

if ((k > hi) || (j < m) && (a[j] < a[k])) // lazy!

temp[i++] = a[j++]; // von links

else temp[i++] = a[k++]; // von rechts

for (int i=0; i<temp.length; i++) // Kopiere zurueck

a[lo+i] = temp[i]; // von Hilfs-Array

}

} // mergeSort

Page 41: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 41

Merge-Sort (4): Analyse

Aufwandsabschätzung:

• Die Folgen werden in allen Fällen (best, worst, average) immer gleichmäßig

aufgeteilt.

• Das Zusammen-Mischen erfordert einen linearen Aufwand in der Länge der

Ergebnisfolge.

• Für die Laufzeit ergibt sich:

T(n) ≤ 2 T(n/2) + O(n), also T(n) O(n log n)

• Platzbedarf: Es wird ein zweites Array benötigt sowie Speicherplatz (Stack) zur

Verwaltung der rekursiven Aufrufe.

Page 42: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 42

Merge-Sort (5): Optimierungsmöglichkeiten

Optimierungsansätze:

• Der Merge-Sort Algorithmus – wie angegeben – ist übersichtlich und leicht

verständlich. Er kann aber zu Lasten dieser Eigenschaften weiter optimiert werden.

• Wie bei Quicksort, könnten weitere einfache Fälle ( n {1, 2, 3, … } ) direkt

behandelt werden.

• Das ständige Neu-Generieren (und Verwerfen) von Hilfs-Arrays verbraucht unnötig

Zeit. Effizienter ist die (Wieder-) Verwendung eines einzigen Arrays der gleichen

Größe wie das Ausgangs-Array. Zudem kann man Kopier-Operationen einsparen.

• Ist eine Teilfolge beim Mischen ausgeschöpft, kann der Rest der anderen ohne

unnötige Abfragen umkopiert werden.

Bemerkungen zur Anwendung:

• Im Vergleich zu anderen internen Sortierverfahren schneidet Merge-Sort nicht so gut

ab, weshalb es selten tatsächlich eingesetzt wird.

• Gut an Merge-Sort ist sein garantiertes worst case Verhalten.

• Varianten von Merge-Sort werden häufig beim Externen Sortieren eingesetzt.

Page 43: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 43

Distribution-Sort (1)

• Bisher betrachtete Sortierverfahren sind Allgemeine Verfahren: Die erlaubten

Operationen sind Schlüsselvergleiche und Elementvertauschungen.

• Allgemeine Sortierverfahren benötigen im worst case immer Zeit in Ω(n log n), bei

einigen kann O(n log n) garantiert werden.

• Distribution-Sort (Sortieren durch Fach-Verteilen) kommt ganz ohne Vergleiche aus.

• Dafür ist ein Zugriff auf die Struktur des Schlüssels erlaubt.

• Die zu sortierenden Elemente werden mehrfach nach jeweils einem Teil des

Schlüssels in Fächer verteilt und wieder zusammengetragen.

• Vorbilder sind klassische mechanische Sortieranlagen etwa für Briefe nach

Postleitzahlen.

Page 44: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 44

Distribution-Sort (2)

Nachteile:

• Es ist nicht ganz leicht, das Vorbild (Fach-Verteilen von Briefen) überhaupt auf den Rechner zu übertragen.

• Im konkreten Fall kann es schwer (oder sehr aufwendig) sein, das Verfahren an einen bestimmten Sortierschlüssel anzupassen.

Vorteile:

• Die besondere Effizienz des Verfahrens.

• Die Laufzeit von Distribution-Sort ist linear in der Größe der Eingabe.

• Wenn anwendbar, ist Distribution-Sort auch in der Praxis den bisherigen Verfahren überlegen.

Page 45: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 45

Distribution-Sort (3): Prinzip

Prinzip beim Sortieren von Briefen:

1. Die letzte Ziffer der Postleitzahl sei die aktuelle Ziffer.

2. Verteile alle Briefe in 10 Fächer mit Nummern 1, 2, . . . , 10 entsprechend der

aktuellen Ziffer.

3. Lege alle Briefe unter Beibehaltung der bisherigen Ordnung zusammen.

4. Ist die aktuelle Ziffer die erste Ziffer, dann sind alle Briefe fertig sortiert.

Ist das nicht der Fall, wird die Ziffer links der aktuellen Ziffer zur neuen aktuellen

Ziffer, und es geht weiter bei Schritt 2

Page 46: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 46

Distribution-Sort: Beispiel

1 2 3, 2 3 1, 3 1 2, 1 2 4, 2 4 1, 4 1 2, 2 3 4, 3 4 2, 4 2 3

Page 47: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 47

Distribution-Sort (3): Beispiel

1 2 3

2 3 1

3 1 2

1 2 4

2 4 1

4 1 2

2 3 4

3 4 2

4 2 3

2 3 1

2 4 1

3 1 2

4 1 2

3 4 2

1 2 3

4 2 3

1 2 4

2 3 4

3 1 2

4 1 2

1 2 3

4 2 3

1 2 4

2 3 1

2 3 4

2 4 1

3 4 2

1 2 3

1 2 4

2 3 1

2 3 4

2 4 1

3 1 2

3 4 2

4 1 2

4 2 3

Beispiel für 3-stellige Postleitzahlen:

Nach 3 (# der Ziffern) Durchgängen sind alle Zahlen sortiert.

Page 48: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 48

Distribution-Sort (4)

Probleme bei der Umsetzung:

• Man muss beachten, wie der Schlüssel aufgebaut ist (Folge von Ziffern, Bytes,

Bits, . . . ) und ob die Sortierung nach den Teilen eine Sortierung der Schlüssel ergibt.

Dies ist etwa für Zahlen in Zweierkomplement-Darstellung nicht gegeben, kann durch

Anpassung aber erreicht werden.

• Bei Schlüsselzerlegung in Bytes z.B. benötigt man 256 Fächer, wovon jedes im

Prinzip alle Datensätze aufnehmen können muss.

Durch Vorinspektion kann aber die # der Datensätze für jedes Fach – und damit

seine Index-Position im Array – ermittelt werden.

Page 49: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 49

Distribution-Sort (5)

count: - … a b c d e f g h … # 1 0 1 1 1 1 3 1 1 2 # 1 1 2 3 4 5 8 9 10 12

Distribution-Sort für char[] mit 256 Fächern (1 Durchgang):

static void distributionSort (char[] a){ int hi = a.length-1;

char[] b = new char[hi+1]; // Hilfs-Array zum Umkopieren

int[] count = new int[256]; // Zaehler, 0 initialisiert

for (int i=0; i <= hi; i++)

count[ (int) a[i] ]++; // Zeichen zaehlen in Fach

for (int i=1; i < 256; i++)

count[i] += count[i-1]; // Akkumulieren

for (int i = hi; i >= 0; i--) // von hinten nach vorne:

b[--count[ (int) a[i] ]] = a[i]; // umsortieren von a nach b

System.arraycopy(b,0, a,0, hi+1); // Array b nach a kopieren

} // distributionSort

Page 50: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 50

• Der angegebene Algorithmus distributionSort interpretiert einzelne Zeichen (char) als Bytes, funktioniert also nur bei Codierungen im Bereich 0, . . . , 255.

• Bei längeren Schlüsseln, die wirklich zerlegt werden müssen, sind entsprechend mehr Durchgänge erforderlich.

• Die Anzahl der Durchgänge ist eine Konstante.

• Pro Durchgang werden linear viele Operationen ausgeführt.

• Die Komplexität des Verfahrens ist in O(n).

• Die Bedingung einer Zerlegbarkeit der Schlüssel in Teile, deren Sortierung die Ordnung auf den Schlüsseln erhält, muss erfüllbar sein, damit das Verfahren angewandt werden kann, wie etwa bei – 5-stelligen positiven Zahlen (Postleitzahlen) – Strings der Länge 20 (fest, aber beliebig) – ganzen und reellen Zahlen (nach Anpassung)

• Das Verfahren lässt sich noch weiter optimieren, etwa durch Vermeiden des Rückkopierens aus dem Hilfs-Array. Statt dessen wird in aufeinander folgenden Durchgängen die Rolle der beiden Arrays vertauscht.

Distribution-Sort (6)

Page 51: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 51

Distribution-Sort (7)

Distribution-Sort für char[] mit 2 Fächern (8 Durchgänge, 1 pro Bit):

static void distributionSortBit (char[] a){

int hi = a.length-1;

char[] b = new char[hi+1]; // Hilfs-Array zum Umkopieren

for (int j=0; j< 8; j++){ // fuer jedes Bit des Schluessels:

int[] count = new int[2]; // Zaehler, 0 initialisiert

for (int i=0; i <= hi; i++)

count[ (a[i]>>>j) & 1 ]++; // Zeichen zaehlen in Fach

for (int i=1; i < 2; i++)

count[i] += count[i-1]; // Akkumulieren

for (int i = hi; i >= 0; i--) // von hinten nach vorne:

b[--count[(a[i]>>>j) & 1]] = a[i]; //umsortieren: a->b

char[] h = a; a = b; b = h; //Array-Referenzen umsetzen

} //for

} // distributionSortBit

Page 52: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 52

Distribution-Sort (8)

Distribution-Sort für char[] mit 2 Fächern (8 × 2 Durchgänge, ohne count-Array):

static void distributionSortBit2 (char[] a){

int hi = a.length-1;

char[] b = new char[hi+1]; // Hilfs-Array zum Umkopieren

for (int j=0; j< 8; j++){ // fuer jedes Bit j des

// Schluessels:

for (int k=1, m=hi; k >= 0; k--) // fuer k in {1,0}

for (int i = hi; i >= 0; i--) // i Zeiger in a:

if ((a[i]>>>j & 1) == k) // j-tes Bit == k?

b [m--] = a [i]; // umsortieren a->b

char[] h = a; a = b; b = h; // Array-Referenzen

// umsetzen

} //for

} // distributionSortBit2

Page 53: Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 10 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.

Informatik II: Algorithmen und Datenstrukturen, SS 2008Prof. Dr. Thomas Ottmann 53

Auswahl von Sortier-Verfahren

Welches ist das beste?

• Bei wenigen Datensätzen (≤ 1000) ist die Laufzeit kaum problematisch, das

einfachste Verfahren kann bevorzugt werden (Insertion, Selection, Bubble, . . . ).

• Sind die Daten fast sortiert, sollte ein Verfahren genommen werden, das

Vorsortierung ausnutzt (Insertion, Bubble, . . . ).

• Sind sehr viele zufällig sortierte Elemente häufig zu sortieren, ist der Aufwand zur

Anpassung von Distribution-Sort gut investiert.

• Will man möglichst flexibel bleiben und hat keine Angst vor dem worst case, kann

man Quick-Sort einsetzen.

• In den restlichen Fällen wird man Shell-Sort, Merge-Sort oder Heap-Sort wählen.