4 Sortierverfahren 4 1 Einfhrung 4 2 Naive


![Die Menge X wird in der Regel als Array [1. . n] of El Die Menge X wird in der Regel als Array [1. . n] of El](https://slidetodoc.com/presentation_image_h/335489f3faf197c9012b35318cabbdd4/image-3.jpg)






![Implementierung in Java: /** Sortieren durch Einfuegen */ void Insertion. Sort(int[] s) { for Implementierung in Java: /** Sortieren durch Einfuegen */ void Insertion. Sort(int[] s) { for](https://slidetodoc.com/presentation_image_h/335489f3faf197c9012b35318cabbdd4/image-10.jpg)


![Implementierung in Java: /** Bubble. Sort */ void bubble. Sort(int[] s) { for (int Implementierung in Java: /** Bubble. Sort */ void bubble. Sort(int[] s) { for (int](https://slidetodoc.com/presentation_image_h/335489f3faf197c9012b35318cabbdd4/image-13.jpg)





![Implementierung in Java: /** Auswahl-Sortieren */ void selection. Sort(int[] s) { for (int i=0; Implementierung in Java: /** Auswahl-Sortieren */ void selection. Sort(int[] s) { for (int i=0;](https://slidetodoc.com/presentation_image_h/335489f3faf197c9012b35318cabbdd4/image-19.jpg)












![Java-Implementation (1) /** Quick. Sort */ void quick. Sort(int[] s, int li, int re) Java-Implementation (1) /** Quick. Sort */ void quick. Sort(int[] s, int li, int re)](https://slidetodoc.com/presentation_image_h/335489f3faf197c9012b35318cabbdd4/image-32.jpg)
![Java-Implementation (2) do { while ((i <= re) && (s[i] <= pivot)) i++; while Java-Implementation (2) do { while ((i <= re) && (s[i] <= pivot)) i++; while](https://slidetodoc.com/presentation_image_h/335489f3faf197c9012b35318cabbdd4/image-33.jpg)


- Slides: 35

4 Sortierverfahren 4. 1 Einführung 4. 2 Naive Sortierverfahren 4. 2. 1 Insertion. Sort (Sortieren durch Einfügen) 4. 2. 2 Bubble. Sort (Aufsteigen von Blasen) 4. 2. 3 Selection. Sort (Sortieren durch Auswählen) 4. 2. 4 Vergleich der naiven Sortierverfahren 4. 3 Effiziente Sortierverfahren "divide and conquer" 4. 3. 1 Merge. Sort 4. 3. 2 Quick. Sort 4. 3. 3 Vergleich verschiedener Sortierverfahren 4. 4 Ein untere Schranke für Sortierverfahren 1

4. 1 Einführung Zuerst: Verfahren, die keine besondere Datenstruktur erfordern, sondern nur Array oder lineare Liste. Das Sortierproblem: Gegeben: • eine Menge X von Elementen xi (i = 1, . . . , n) und • eine Abbildung k: X -> C ("key" / Schlüssel), wobei auf dem Bildbereich C eine Ordnungsrelation "<<" definiert ist. Gesucht: • eine Anordnung der Elemente von X als xi', so dass für alle i < j: k(xi') << k(xj'). 2
![Die Menge X wird in der Regel als Array 1 n of El Die Menge X wird in der Regel als Array [1. . n] of El](https://slidetodoc.com/presentation_image_h/335489f3faf197c9012b35318cabbdd4/image-3.jpg)
Die Menge X wird in der Regel als Array [1. . n] of El oder List of El repräsentiert, wobei El der Typ der zu sortierenden Elemente ist. El kann selbst zusammengesetzt sein (z. B. Verbundtyp oder Class). Vereinfachung (reicht zur Erläuterung von Sortieralgorithmen): die Elemente von X sind selbst Zahlen mit der üblichen Ordnung. Also: X[i] < X[j] anstelle von k(xi) << k(xj). 3

Allgemeiner Lösungsansatz Viele Sortierverfahren sind Variationen des folgenden Grundmusters: Solange es Indexpaare (i, j) mit 1 i < j n und X[i] > X[j] gibt, vertausche X[i] mit X[j]. Daher benötigt: Grundoperationen für • paarweisen Vergleich zweier Elemente sowie für das • Vertauschen. Dies geschieht im Array üblicherweise mittels einer Vertauschoperation swap(Array, i, j), die als destruktive (nicht kopierende) Prozedur realisiert wird. Das Sortieren von Listen wird meist funktional implementiert, wobei der Listenparameter jeweils kopiert wird. Grundoperationen (Funktionen) sind hier: • isempty(List) -> Boolean • first(List) -> El • but. First(List) -> List • insert. First(El, List) -> List (statt insert. First auch: cons(El, List)) 4

Bemerkung zur Indizierung In der obigen Definition des Sortierproblems ist zur Ablage der Menge X von einem Feld Array[1. . n] ausgegangen worden. In Programmiersprachen wie C oder Java werden allerdings Felder mit n Elementen von 0 bis n-1 indiziert. Dies ist bei der Verwendung dieser Programmiersprachen in der Definition entsprechend anzupassen (vgl. auch die Beispiele in Java; dort wird auf das Element s[0] zugegriffen!). 5

Terminologie • Sortieren in situ (am Ort): Die zu sortierenden Datensätze werden in einem Array gehalten und nicht kopiert. • Stabiles Sortierverfahren: Elemente mit gleichem Schlüssel werden nicht vertauscht. • Internes vs. externes Sortieren: Die Daten liegen vollständig im Hauptspeicher vor bzw. nicht. (Externes Sortieren ist - obwohl hier zunächst nicht behandelt - von größerer praktischer Bedeutung!) • Allgemeingültige und spezielle Verfahren: Erstere beruhen auf dem paarweisen Vergleich von Elementen (d. h. ihrer Schlüssel) und machen - im Gegensatz zu den speziellen - keine Annahmen über den Wertebereich und die Struktur der Elemente. 6

4. 2 Naive Sortierverfahren Drei „naive“ Sortierverfahren“: • Insertion. Sort (Sortieren durch Einfügen) • Bubble. Sort (Aufsteigen von Blasen) • Selection. Sort (Sortieren durch Auswählen) Effizienzanalyse jeweils: • günstigster Fall (best case), • ungünstigster Fall (worst case) und • durchschnittlicher Fall (average case: "av"). 7

4. 2. 1 Insertion. Sort (Sortieren durch Einfügen) Zu sortieren: eine Folge x 1, . . . , xn. Idee: Für i=2 bis n do /** Annahme: die Teilfolge x 1, . . . , xi-1 ist sortiert */ füge xi an der richtigen Stelle in die Teilfolge x 1, . . . , xi-1 ein. Insertion. Sort ist ein stabiles in situ-Verfahren. 8

algorithm Insertion. Sort(s: List of El) -> List of El { wenn isempty(s) dann rückgabe s; rückgabe insert(first(s), insertion. Sort(but. First(s)) } function insert(x: El, s: List of El) -> List of El { wenn isempty(s) dann rückgabe list(x); wenn x < first(s) dann rückgabe insert. First(x, s) sonst rückgabe insert. First(first(s), insert(x, but. First(s)) } Die Funktion insert(x: El, s: List) setzt s als vorsortierte Liste voraus. 9
![Implementierung in Java Sortieren durch Einfuegen void Insertion Sortint s for Implementierung in Java: /** Sortieren durch Einfuegen */ void Insertion. Sort(int[] s) { for](https://slidetodoc.com/presentation_image_h/335489f3faf197c9012b35318cabbdd4/image-10.jpg)
Implementierung in Java: /** Sortieren durch Einfuegen */ void Insertion. Sort(int[] s) { for (int i=1; i < s. length; i++) for (int j=i; (j > 0) && (s[j] < s[j-1]); j--) Swap(s, j, j-1); } 10

Effizienzanalyse: • günstigster Fall (best case), hier: aufsteigende Vorsortierung, • ungünstigster Fall (worst case), hier: absteigende Vorsortierung, • durchschnittlicher Fall (average case: "av"). • Zahl der Vergleiche: C(n) (comparisons) • Zahl der Vertauschungen: S(n) (swaps). Sbest = 0 = O(1) Sav = n(n-1) / 4 = O(n²) Sworst = n (n-1) / 2 = O(n²) Cbest = n-1 = O(n) Cav = Sav + (n-1) = n (n-1) / 4 + (n-1) = O(n²) Cworst = n (n-1) / 2 = O(n²) 11

4. 2. 2 Bubble. Sort (Aufsteigen von Blasen) Zu sortieren: eine Folge x 1, . . . , xn. Idee: Durchlaufe das Feld von hinten nach vorne und vertausche zwei benachbarte Elemente, wenn sie nicht in der korrekten Reihenfolge stehen. In jedem Einzelschritt rückt das kleinere zweier benachbarter Elemente nach links. Nach einem Durchlauf ist sichergestellt, dass das kleinste Elemente ganz links steht; nach i Durchläufen stehen die i kleinsten Elemente (aufsteigend) an den Positionen 0 bis i-1. Bubble. Sort ist ein stabiles in situ-Verfahren. 12
![Implementierung in Java Bubble Sort void bubble Sortint s for int Implementierung in Java: /** Bubble. Sort */ void bubble. Sort(int[] s) { for (int](https://slidetodoc.com/presentation_image_h/335489f3faf197c9012b35318cabbdd4/image-13.jpg)
Implementierung in Java: /** Bubble. Sort */ void bubble. Sort(int[] s) { for (int i=0; i < s. length-1; i++) for (int j=s. length-1; j>i; j--) if (s[j] < s[j-1]) Swap(s, j, j-1); } 13

Namenserklärung „Bubble“-Sort: Bei umgekehrtem Durchlaufen: „Aufsteigen von Blasen“ (engl. bubble): Größere Blasen steigen solange auf, bis sie durch eine noch größere Blase aufgehalten werden, die ihrerseits wieder aufsteigt. 14

Effizienzanalyse: • Zahl der Vergleiche: C(n) (comparisons) • Zahl der Vertauschungen: S(n) (swaps). Cbest = n (n-1) / 2 = O(n²) Cav = n (n-1) / 2 = O(n²) Cworst = n (n-1) / 2 = O(n²) Sbest = 0 = O(1) Sav = n (n-1) /4 = O(n²) Sworst = n (n-1) / 2 = O(n²) 15

4. 2. 3 Selection. Sort (Sortieren durch Auswählen) Zu sortieren: eine Folge x 1, . . . , xn. Idee: 1. 2. Schritt: ermittle das kleinste Element der Folge und setze es an die erste Stelle. Schritt: bestimme das kleinste Element der verbleibenden Folge mit n-1 Elementen und setze es an die zweite Stelle. Etc. Nach n Schritten ist die Folge sortiert. 16

Vergleich Insertion. Sort und Selection. Sort Bei Insertion. Sort • Auswahl: leicht. Das erste (beliebige) Element aus dem unsortierten Teil des Arrays ausgewählt; • Einfügen in den sortierten Teil: aufwändig. Bei Selection. Sort • Auswahl: aufwändig. • Einfügen: leicht. Dadurch ist die Zahl der Swap-Operationen minimal (immer n-1). 17

Funktionale Implementierung algorithm selection. Sort(s: List of El) -> List of El { wenn isempty(s) dann rückgabe s; rückgabe insert. First(min(s), selection. Sort(but. First(replace(s, min(s), first(s)))) } function replace(s: List of El, x: El, y: El) -> List of El { wenn isempty(s) dann rückgabe s; wenn x = first(s) dann rückgabe insert. First(y, but. First(s)); rückgabe insert. First(first(s), replace(but. First(s), x, y)) } 18
![Implementierung in Java AuswahlSortieren void selection Sortint s for int i0 Implementierung in Java: /** Auswahl-Sortieren */ void selection. Sort(int[] s) { for (int i=0;](https://slidetodoc.com/presentation_image_h/335489f3faf197c9012b35318cabbdd4/image-19.jpg)
Implementierung in Java: /** Auswahl-Sortieren */ void selection. Sort(int[] s) { for (int i=0; i < s. length-1; i++) { int min_index = i; for (int j=i+1; j < s. length; j++) if (s[j] < s[min_index]) min_index = j; Swap(s, i, min_index); } } 19

Effizienzanalyse: Cbest = n (n-1) / 2 = O(n 2) Cav = n (n-1) /2 = O(n 2) Cworst = n (n-1) / 2 = O(n 2) Sbest = n-1 = O(n) Sav = n-1 = O(n) Sworst = n-1 = O(n) 20

4. 2. 4 Vergleich der naiven Sortierverfahren Insertion Sort Vergleiche: Swaps: Bubble Sort Selection Sort best O(n) O(n²) average O(n²) worst O(n²) best O(1) O(n) average O(n²) O(n) worst O(n²) O(n) 21

4. 3 Effiziente Sortierverfahren "divide and conquer" Prinzip "divide and conquer" (teile und herrsche): • Teile (divide) die zu sortierende Folge solange weiter auf, bis triviale Fälle entstehen. • diese direkt behandeln (conquer), • dann zusammenfügen (merge). Führt zu verzweigt rekursiven Verfahren. 22

Merge. Sort: Quick. Sort Aufwand in Aufwand in merge-Phase divide-Phase: 23

4. 3. 1 Merge. Sort (Sortieren durch Verschmelzen) algorithm merge. Sort(s: List of El) -> List of El { n: int; r, t, r', t': List of El; n : = länge(s); wenn n ≤ 1 dann rückgabe s; r : = <s 1, . . . , s[n/2] >; t : = <s[n/2]+1, . . . , sn>; r' : = merge. Sort(r); t' : = merge. Sort(t); rückgabe merge(r', t') } function merge(r: List of El, s: List of El) -> List of El { wenn isempty(r) dann rückgabe s; wenn isempty(s) dann rückgabe r; wenn first(r) < first(s) dann rückgabe insert. First(first(r), merge(but. First(r), s) sonst rückgabe insert. First(first(s), merge(r, but. First(s)) } 24

Eigenschaften • Merge. Sort: beliebt für externes Sortieren. • Vorteil gegenüber Quick. Sort (später): gleichmäßige Aufteilung (divide) leicht möglich. 25

Effizienzanalyse: Zuerst von merge: function merge(r: List of El, s: List of El) -> List of El { wenn isempty(r) dann rückgabe s; wenn isempty(s) dann rückgabe r; wenn first(r) < first(s) dann rückgabe insert. First(first(r), merge(but. First(r), s) sonst rückgabe insert. First(first(s), merge(r, but. First(s)) } Komplexität von merge: O(n+m), wobei n und m die entsprechenden Längen der Listen sind. 26

Effizienzanalyse: Je nach Implementierung erhält man für die Komplexität von Merge. Sort folgende Rekursionsgleichung (n = Zahl der Listenelemente): • T(n) = O(n) + 2 * T(n/2) + O(n) bei Implementierung als Liste mit divide in O(n) • T(n) = O(1) + 2 * T(n/2) + O(n) bei Implementierung als Array mit divide in O(1) Dies lässt sich zu einer Rekursionsgleichung verallgemeinern, die für viele Verfahren mit "balancierter Zerlegung" in zwei Teilprobleme gilt: T(n) = a (konst. ) für n = 1 T(n) = 2 * T(n/2) + f(n) + c sonst 27

Lösung der Rekursionsgleichung T(1) = a (konst. ) T(n) = 2 • T(n/2) + f(n) + c für n >1 Annahme: i f(n/i) f(n) Gilt z. B. für f(n) = nk • log(n) j (k 1, j 0). Lösung der Rekursionsgleichung: ersichtlich aus Anordnung der f(n) in einem Binärbaum mit k = log n Ebenen: T(n) = O(n) falls f(n) = 0 T(n) = O(n log n) falls f(n) = b • n T(n) = O(n log² n) falls f(n) = b • n • log n 28

29

Quick. Sort (1) • Das algorithmische Prinzip von Quick. Sort in funktionaler Darstellung: • algorithm quick. Sort(s: List of El) -> List of El { wenn länge(s) ≤ 1 dann rückgabe s; ("partitioniere" s in r und t, sodass r ≤ t elementweise); r' : = quick. Sort(r); t' : = quick. Sort(t); rückgabe concat(r', t') } • Aufwand O(n log n) (average case) 30

Quick. Sort (2) • Problem: Gleichmäßige Aufteilung der Listen • Dazu wählt man Pivot p mit r ≤ p ≤ t • Beim Sortieren werden Teillisten als Sub-Arrays dargestellt. • Es wird ein Pivot-Wert gewählt, der auch wirklich im Array vorkommt. Dies geschieht in einer Funktion pivot_index, die den Index des Pivot-Wertes im Array zurückgibt. • partition wird als Funktion realisiert, die den Index der Nahtstelle mit dem Pivot-Element zurückliefert. Das Pivot -Element selbst wird mittels zweier zusätzlicher "Swaps" aus dem Vergleichprozess herausgehalten. • Sortierroutine (Quick. Sort) verzweigt rekursiv. • Kleine Restlisten werden mit Insert. Sort zu Ende sortiert 31
![JavaImplementation 1 Quick Sort void quick Sortint s int li int re Java-Implementation (1) /** Quick. Sort */ void quick. Sort(int[] s, int li, int re)](https://slidetodoc.com/presentation_image_h/335489f3faf197c9012b35318cabbdd4/image-32.jpg)
Java-Implementation (1) /** Quick. Sort */ void quick. Sort(int[] s, int li, int re) { System. out. print("["+li+", "+re+"]: "); print. Array(s); int m = partition(s, li, re); if ((m-li) > 1) quick. Sort(s, li, m-1); if ((re-m) > 1) quick. Sort(s, m+1, re); } int partition(int[] s, int li, int re) // wichtig: li < re { / / if (li == re-1) // { if (s[li] > s[re]) Swap(s, li, re); // return li; } int p = pivot_index(s, li, re); int pivot = s[p]; Swap(s, p, li); // bringe Pivot-Element an den Anfang int i = li+1; // NICHT li++ !!! int j = re; 32
![JavaImplementation 2 do while i re si pivot i while Java-Implementation (2) do { while ((i <= re) && (s[i] <= pivot)) i++; while](https://slidetodoc.com/presentation_image_h/335489f3faf197c9012b35318cabbdd4/image-33.jpg)
Java-Implementation (2) do { while ((i <= re) && (s[i] <= pivot)) i++; while ((j >= li+1) && (pivot <= s[j])) j--; if (i < j) Swap(s, i, j); } while (i < j); // jetzt gilt: j =< i ! Swap(s, li, j); // bringe Pivot-Element an Nahtstelle return j; } int pivot_index(int[] s, int li, int re) { double m = (s[li] + s[re]) / 2. 0; double d = Math. abs(s[li] - m); int p = li; for (int i=li+1; i<=re; i++) if (Math. abs(s[i]-m) < d) { p = i; d = Math. abs(s[i]-m); } return p; } /* --- alternative Implementierung int pivot_index(int[] s, int li, int re) { return (li + re) / 2; } --- */ 33

Untere Schranke für Sortierverfahren Sortieren von 3 Elementen: Für jede Permutation von x, y, z existiert (mindestens) ein Blatt des Entscheidungsbaumes 34

Untere Schranke für Sortierverfahren 2 Benutze nun die folgende Abschätzung: n! > n(n-1). . . (n/2) = (n/2) n/2 log n! > (n/2)(log n – log 2) Die Höhe eines binären Baumes mit m Blättern ist aber mindestens élog 2 m . Damit ist klar, dass es für mindestens eine Anfangskonstellation nötig ist, einen Pfad der Länge > C n log n zu durchlaufen, um die Menge basierend auf Vergleichen zu sortieren. Eine bessere Abschätzung für n! liefert übrigens die Stirlingsche Formel log n! 0. 5 log (2 n) + n (log n – 1) 35