Insertionsort (Sortieren durch Einfügen) ist ein elementares Sortierverfahren. Es hat eine Zeitkomplexität von Θ(n2), ist damit also langsamer als etwa Heapsort, Mergesort oder auch Shellsort. Sehr gut geeignet ist Insertionsort für das Sortieren von kleinen Datenmengen oder für das Einfügen von weiteren Elementen in eine schon sortierte Folge.
Zu Anfang und nach jedem Schritt des Verfahrens besteht die zu sortierende Folge a0, ..., an-1 aus zwei Teilen: Der erste Teil a0, ..., ai-1 ist bereits aufsteigend sortiert, der zweite Teil ai, ..., an-1 ist noch unsortiert.
Das Element ai wird als nächstes in den bereits sortierten Teil eingefügt, indem es der Reihe nach mit ai-1, ai-2 usw. verglichen wird. Sobald ein Element aj mit aj ≤ ai gefunden wird, wird ai hinter diesem eingefügt. Wird kein solches Element gefunden, wird ai an den Anfang der Folge gesetzt.
Damit ist der sortierte Teil um ein Element länger geworden. Im nächsten Schritt wird ai+1 in den sortierten Teil eingefügt usw. Zu Anfang besteht der sortierte Teil nur aus dem Element a0; zum Schluss aus allen Elementen a0, ..., an-1.
Beispiel: Die folgende Tabelle zeigt die Sortierschritte zum Sortieren der Folge 5 7 0 3 4 2 6 1. Auf der linken Seite grün dargestellt befindet sich jeweils der bereits sortierte Teil der Folge. Ganz rechts steht in Klammern die Anzahl der Positionen, um die das eingefügte Element nach links gewandert ist.
5 | 7 | 0 | 3 | 4 | 2 | 6 | 1 | (0) | |
5 | 7 | 0 | 3 | 4 | 2 | 6 | 1 | (0) | |
0 | 5 | 7 | 3 | 4 | 2 | 6 | 1 | (2) | |
0 | 3 | 5 | 7 | 4 | 2 | 6 | 1 | (2) | |
0 | 3 | 4 | 5 | 7 | 2 | 6 | 1 | (2) | |
0 | 2 | 3 | 4 | 5 | 7 | 6 | 1 | (4) | |
0 | 2 | 3 | 4 | 5 | 6 | 7 | 1 | (1) | |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | (6) |
Die folgende Funktion insertionsort sortiert ein Integer-Array a[0], ..., a[n-1].
Die Sortierfunktion ist in der Klasse InsertionSorter gekapselt. Mit den Anweisungen
wird ein Objekt vom Typ InsertionSorter erzeugt und anschließend die Methode sort zum Sortieren eines Arrays b aufgerufen.
Im schlechtesten Fall wird der Platz für das einzufügende Element immer erst ganz am Anfang des sortierten Teils gefunden. D.h. in der While-Schleife werden Folgen der Länge 1, 2, 3, ..., n-1 durchsucht. Insgesamt sind dies (n-1)·n / 2 Schritte, also Θ(n2) Schritte. Dieser Fall tritt ein, wenn die Folge zu Anfang in absteigender Reihenfolge sortiert ist.
Es ginge auch schneller, die Einfügeposition des Elements ai innerhalb des sortierten Teils a0, ..., ai-1 zu finden, nämlich mit binärer Suche. Da aber die größeren Elemente alle nach rechts rücken müssen, um die Einfügeposition frei zu machen, ist für das Einfügen ohnehin lineare Zeit erforderlich.
Die genaue Anzahl der Schritte, die Insertionsort benötigt, wird durch die Anzahl der Inversionen der zu sortierenden Folge bestimmt.
Definition: Sei a = a0, ..., an-1 eine endliche Folge. Eine Inversion ist ein Paar (i, j) mit i < j und ai > aj. Eine Inversion ist also ein Paar von Indexpositionen, an denen die Elemente der Folge in falscher Reihenfolge stehen.1)
Beispiel: Sei a = 5, 7, 4, 9, 7. Dann ist (0, 2) eine Inversion, denn es ist a0 > a2, nämlich 5 > 4. Außerdem sind (1, 2) und (3, 4) Inversionen, da 7 > 4 und 9 > 7. Weitere Inversionen sind nicht vorhanden.
Wir bestimmen nun die Anzahl der Inversionen (i, j) der Folge a getrennt für jede Position j. Ergebnis ist jeweils ein Wert vj, der die Anzahl der Elemente ai angibt, die links von aj stehen und größer sind als aj.
In der Folge a = 5, 7, 4, 9, 7 stehen beispielsweise links von a2 = 4 die zwei größeren Zahlen 5 und 7, also ist v2 = 2. Links von a4 = 7 steht nur eine größere Zahl, also ist v4 = 1.
Die Folge der vj wird als Inversionenfolge bezeichnet.
Definition: Die Inversionenfolge v = v0, ..., vn-1 einer Folge a = a0, ..., an-1 ist definiert durch
vj = |{ (i, j) | i < j ∧ ai > aj }|
für j = 0, ..., n-1.
Beispiel: Die obige Folge a = 5, 7, 4, 9, 7 hat die Inversionenfolge v = 0, 0, 2, 0, 1.
Offensichtlich gilt vi ≤ i für alle i = 0, ..., n-1. Genau dann, wenn alle vi gleich 0 sind, ist die zugehörige Folge a sortiert. Ist die Folge a eine Permutation, so ist sie durch ihre Inversionenfolge v eindeutig bestimmt. Die Permutation n-1, ..., 0 hat die Inversionenfolge 0, ..., n-1.
Satz: Sei a = a0, ..., an-1 eine Folge und v = v0, ..., vn-1 ihre Inversionenfolge. Dann ist die Anzahl der Schritte, die Insertionsort zum Sortieren der Folge benötigt
T(a) = i = 0, ..., n-1 vi
Beweis: Offensichtlich benötigt Insertionsort in jeder Iteration i gerade vi Schritte, um das Element ai einzufügen. Daher ist die Gesamtzahl der benötigten Schritte gleich der Summe aller vi.
Beispiel: Die folgende Tabelle zeigt die Folge a aus dem Anfangsbeispiel und die zugehörige Inversionenfolge.
Beispielsweise ist v5 = 4, weil vier Elemente links von a5 = 2 stehen, die größer als 2 sind (nämlich 5, 7, 3 und 4). Entsprechend benötigt Insertionsort zum Einfügen der 2 genau 4 Schritte.
Die Summe aller vi, also die Gesamtzahl aller Inversionen, ist 17. Insertionsort benötigt also zum Sortieren der Folge 17 Schritte.
Aufgabe 1:
Aufgabe 2:
Das Programm lässt sich noch besser strukturieren, indem das Einsortieren eines Elements t in das schon sortierte Anfangsstück a0, ..., ai-1 in eine Funktion insert ausgelagert wird. Das Element t kommt an die Position i, wenn ai-1 kleiner oder gleich t ist; wenn dagegen ai-1 größer als t ist, rückt ai-1 nach rechts und i wird um 1 vermindert – solange wie i ≥ 1 gilt.
Das Nach-Rechts-Rücken ist hier etwas speziell implementiert: Die Anweisung a[i]=a[--i]; ist gleichbedeutend mit der Anweisungsfolge a[i]=a[i-1]; i=i-1;.
[Knu 73] D.E. Knuth: The Art of Computer Programming, Vol. 3 - Sorting and Searching. Addison-Wesley (1973)
[Sed 88] R. Sedgewick: Algorithms. 2. Auflage, Addison-Wesley (1988)
[Lan 12] H.W. Lang: Algorithmen in Java. 3. Auflage, Oldenbourg (2012)
Insertionsort und andere Sortierverfahren, so etwa Quicksort, Heapsort, Mergesort und Shellsort, finden Sie auch in meinem Buch über Algorithmen.
Weitere Themen des Buches: Textsuche, Graphenalgorithmen, Arithmetik, Codierung, Kryptografie, parallele Sortieralgorithmen.
1) Wenn die Folge a eine Permutation ist, lässt sich eine Inversion auch durch (ai, aj) anstelle von (i, j) angeben [Knu 73].
Weiter mit: [Quicksort] oder [up]