Schlagwort-Archive: sortieren

Algorithmen und Datenstrukturen: Der Radix Sort in Java

Der Algorithmus

Der Radix Sort-Algorithmus sortiert Zahlensysteme anhand ihrer Stellen. Der Radix Sort wird daher auch Base Sort genannt. Dies bedeutet, dass je Stelle ein eigenes Sortierverfahren (zumeist Counting Sort weil Wertebereich überschaubar) eingesetzt wird. Man würde also bei 3 Stellen auch 3x sortieren.

Es gibt einen Most- und Least-Significant-Radix-Sort (MSD / LSD Radix Sort). LSD beginnt bei der niedrigsten Stelle (z.B. den Einern) und bewegt sich zu den höheren Stellen (z.B. Zehner, Hunderter). Bei MSD Radix Sort ist das umgekehrt.

Eine wichtige Voraussetzung ist, das man die gleiche Anzahl an Stellen hat (also z.B. 2 (Tupel), 3 (Tripel) oder mehr Stellen). Weiterhin müssen die vorhandenen Elemente in dem Zahlensystem (z.B. Dezimal 0-9) in einer Reihenfolge nach Rang sortierbar sein. D.h. es sollte z.B. klar sein dass ein A vor einem B ist (z.B. durch ASCII-Werte).

Nehmen wir an, wir möchten die Zahlen [170, 45, 75, 90, 802, 24, 2, 66] sortieren:

LSD Radix Sort (von rechts nach links):

  1. Sortierung nach der 1er-Stelle: [170, 90, 802, 2, 24, 45, 75, 66]
  2. Sortierung nach der 10er-Stelle: [802, 2, 24, 45, 66, 170, 75, 90]
  3. Sortierung nach der 100er-Stelle: [2, 24, 45, 66, 75, 90, 170, 802]

    Anwendung: Oft bei der Sortierung von Zahlen und Wörtern verwendet.
    Vorteil: Einfacher zu implementieren und erfordert weniger komplexe Vergleiche.

MSD Radix Sort (von links nach rechts):

  1. Sortierung nach der 100er-Stelle: [170, 24, 2, 45, 66, 75, 802, 90]
  2. Sortierung nach der 10er-Stelle: [2, 24, 45, 66, 75, 90, 170, 802]
  3. Sortierung nach der 1er-Stelle: [2, 24, 45, 66, 75, 90, 170, 802]

    Anwendung: Eignet sich gut für große Zahlenmengen oder lange Zeichenketten.
    Vorteil: Kann effizienter sein, wenn die Daten ungleichmäßig verteilt sind.

Code von LSD Radix Sort

Der folgende Code hat die vergangenen Implementierungen von QuickSort und Counting Sort (ohne Stellensortierung durch Radix / Base Sort) mit einer Zeitmessung in Echtzeit verglichen und muss angepasst werden. Das nachfolgende Beispiel sortiert Dezimalzahlen (also ein Zahlensystem zur Basis 10).

package AlgoDat;

import java.util.Arrays;

public class RadixSort {
    // Zu sortierendes Array
    private static int myArrayRadixSort[] = {22, 6, 2, 4, 10, 3, 9, 7, 5, 8, 1};
    
    public boolean verbose = false;

    // Hält die Klassen als instanziertes Objekt
    @SuppressWarnings("unused")
    private static RadixSort programRadixSort;
    @SuppressWarnings("unused")
    private static HelperUtil helper;

    // Hilfsfunktion für das Ausgeben des Arrays
    public void OutputOfIntArray(int myArray[])
    {
        if (myArray != null)
        {
            for (int i = 0; i < myArray.length; i++) {
                if (i > 0) System.out.print(";");
                System.out.print(myArray[i]);
            }

            System.out.println();
        }
    }

    public void radixSort(int[] arr) 
    { 
        int max = Arrays.stream(arr).max().getAsInt();
        if (verbose) System.out.println("Größte Zahl im Array: " + max);
        
        // Bei Dezimalzahlen muss die Basis 10 genommen werden
        // ... bei Großbuchstaben z.B. die Basis 27
        for (int exp = 1; max / exp > 0; exp *= 10) 
        { 
            if (verbose) System.out.println("Suche " + exp + "er.");
            countingSort(arr, exp); 
        } 
    } 
    
    public void countingSort(int[] arr, int exp) 
    { 
        int n = arr.length; 
        int[] output = new int[n]; 
        int[] count = new int[10]; 
        
        // Alle 10 Elemente mit 0 initialisieren
        for (int i = 0; i < 10; i++) 
        { 
            count[i] = 0; 
        } 
        
        // Histogramm erstellen
        for (int i = 0; i < n; i++) 
        { 
            count[(arr[i] / exp) % 10]++; 
        } 
        
        // Kummuliere, so dass die Anfangspositionen in dem Array stehen
        for (int i = 1; i < 10; i++) 
        { 
            count[i] += count[i - 1]; 
        } 
        
        // Ausgabearray erstellen
        for (int i = n - 1; i >= 0; i--) 
        { 
            output[count[(arr[i] / exp) % 10] - 1] = arr[i]; 
            count[(arr[i] / exp) % 10]--; 
        } 
        
        // Kopiere das neu erstellte Array auf das zu ändernde Array
        for (int i = 0; i < n; i++) 
        { 
            arr[i] = output[i]; 
        } 
    } 
    
    // Konstruktor
    public RadixSort(int[] toSort)
    {
        this.radixSort(toSort);
    }

    // Konstruktor
    public RadixSort()
    {
        System.out.print("Vorher: ");
        this.OutputOfIntArray(myArray);

        this.radixSort(myArray); 
        
        System.out.print("Nachher: ");
        this.OutputOfIntArray(myArray);
    }

    public static void main(String[] args) 
    {
        helper = new HelperUtil();

        // Es gibt 80 Mio Menschen in Deutschland, von denen keiner älter als 120 ist -> Kleiner Wertebereich
        int[] alterDerMenschenInDeutschlandRS = helper.generateIntegerArrayOfSize(80000000, 120);
        int[] alterDerMenschenInDeutschlandQS = alterDerMenschenInDeutschlandRS.clone();
        int[] alterDerMenschenInDeutschlandCS = alterDerMenschenInDeutschlandRS.clone();
        // Erwartung: Da Counting Sort eine lineare Laufzeit hat sollte er bei kleinem Wertebereich am Besten performen
        
        System.out.println("\nEs gibt 80 Mio Menschen in Deutschland, von denen keiner älter als 120 ist -> Kleiner Wertebereich");
        System.out.println("===================================================================");
        
        long startZeitRS = System.nanoTime();
        programRadixSort = new RadixSort(alterDerMenschenInDeutschlandRS);
        long endZeitRS = System.nanoTime();
        System.out.println((endZeitRS-startZeitRS) + " ns (Radix Sort)");
    }
}

Ausgabe

Es gibt 80 Mio Menschen in Deutschland, von denen 
keiner älter als 120 ist -> Kleiner Wertebereich
=======================================================
347584000 ns (Counting Sort)
2544168000 ns (Radix Sort)
3335958900 ns (Quick Sort)

Komplexität: O-Notation (Ordnung)

O(n*l) (l=Länge also bei ABC = Länge: 3 – Anzahl der max. Stellen0)
‣ Sortieren von Strings über einem endlichen Alphabet durch
mehrmaliges Anwenden von Counting Sort.
‣ Angenommen: Alle Wörter haben die Länge
‣ Z.b. Postleitzahlen

Laufzeit von Radix Sort: 𝒪(n), weil l ⋅ n Durchläufe

Algorithmen und Datenstrukturen: Der Counting Sort in Java

Der Algorithmus

Der Counting Sort ist ein Out-of-place-Sortieralgorithmus, da er das zu sortierende Array im Gegensatz zu Quick Sort oder Bubble Sort nicht direkt sortiert, sondern vorher eine Speicherkopie anlegt, die im Abschluss auf das zu sortierende Array kopiert wird.

Der Algorithmus ist genau dann effizient, wenn man viele (doppelte) Zahlen in einem kleinem Wertebereich / einer engen Min-/Max-Range sortieren möchte.

Wenn man z.B. ein Array mit dem Aufkommen von Menschen nach Alter in der deutschen Bevölkerung sortieren möchte, so hätte man eine Array-Größe von ca. 80 Millionen Einträgen. Da es aber keine Menschen über 120 Jahren in Deutschland gibt, oder der älteste noch lebende Mensch in Deutschland aktuell jünger als 120 Jahre ist, kann man den Wertebereich zwischen 0 und 120 Jahren einschränken.

  • Kleiner Wertebereich: Alter zwischen 0 und 120 Jahren.
  • Großes Array: 80 Millionen Einträge für jeden Menschen

Für diese Fälle eignet sich der Counting Sort Algorithmus ausgezeichnet, da er nach der folgenden Vorgehensweise vor geht:

  • Ermittle den maximalen Wert des Wertebereichs für das Histogramm (z.B. höchstes Alter 120 Jahre)
  • Initialisiere ein sortiertes Histogramm als Array mit 0-Werten.
  • Laufe das große Array in einer O(n)-Schleife durch (z.B. das Array mit den 80 Mio. Menschen)
    • Zähle die Histogramm-Array-Werte über die Vorkommnisse/Häufigkeiten beim Durchlaufen um 1 nach oben, je nachdem welche Zahl bei dem Durchlauf gefunden wird
    • Laufe das erstellte Histogramm-Array von links nach rechts durch und füge die Vorkommnisse der Zahlen in das Ziel-Array ein, so dass bei 2x der Zahl 1 und 4x der Zahl 2 so etwas rauskommt: 1;1;2;2;2;2
  • Auf diese Weise wurde anhand des sortierten Histogramm-Arrays die Zahlenmenge sortiert
package AlgoDat;

import java.util.Arrays;

public class CountingSort {
    // Zu sortierendes Array
    private int myArray[] = {22, 6, 2, 4, 10, 3, 9, 7, 5, 8, 1};

    // Hält die Klasse als instanziertes Objekt
    @SuppressWarnings("unused")
    private static CountingSort program;
    private java.util.Random rnd = new java.util.Random();

    // Hilfsfunktion für das Ausgeben des Arrays
    public void OutputOfIntArray(int myArray[])
    {
        if (myArray != null)
        {
            for (int i = 0; i < myArray.length; i++) {
                if (i > 0) System.out.print(";");
                System.out.print(myArray[i]);
            }

            System.out.println();
        }
    }

    // Generiert ein zufälliges Integer-Array der Größe size mit Werten zwischen 0 und upperLimit
    public int[] generateIntegerArrayOfSize(int size, int upperLimit)
    {       
        int[] generatedArray = new int[size];
        
        for (int i = 0; i < generatedArray.length; i++)
        {
            generatedArray[i] = rnd.nextInt(upperLimit);
        }

        return generatedArray;
    }

    // Konstruktor
    public CountingSort()
    {
        System.out.println("Kleines Array");
        System.out.println("=============");
        System.out.print("Vorher: ");
        this.OutputOfIntArray(myArray);

        long startTime = System.nanoTime();
        this.sort(myArray, false);
        long endTime = System.nanoTime();

        System.out.print("Nachher: ");
        this.OutputOfIntArray(myArray);

        System.out.println("Ich habe " + (endTime-startTime) + " ns mit einem Array von " + myArray.length + " und einem Wertebereich/Histogramm von " + Arrays.stream(myArray).max().getAsInt() + " Zahlen gebraucht! \n\n");

        System.out.println("Großes Array, kleiner Wertebereich");
        System.out.println("==================================");
        System.out.print("Vorher: ");
        int[] grossesArray = this.generateIntegerArrayOfSize(10000000, 100000);

        startTime = System.nanoTime();
        this.sort(grossesArray, false);
        endTime = System.nanoTime();

        System.out.println("Ich habe " + (endTime-startTime) + " ns mit einem Array von " + grossesArray.length + " und einem Wertebereich/Histogramm von " + Arrays.stream(grossesArray).max().getAsInt() + " Zahlen gebraucht! \n\n");

        System.out.println("Kleines Array, großer Wertebereich");
        System.out.println("==================================");
        System.out.print("Vorher: ");
        int[] kleinesArray = this.generateIntegerArrayOfSize(100000, 10000000);

        startTime = System.nanoTime();
        this.sort(kleinesArray, false);
        endTime = System.nanoTime();

        System.out.println("Ich habe " + (endTime-startTime) + " ns mit einem Array von " + kleinesArray.length + " und einem Wertebereich/Histogramm von " + Arrays.stream(kleinesArray).max().getAsInt() + " Zahlen gebraucht! \n\n");
    }

    public void sort(int[] myArray) 
    {
        this.sort(myArray, true);
    }

    public void sort(int[] myArray, boolean verbose) 
    {
        //int maxValue = findMax(elements);
        int maxValue = Arrays.stream(myArray).max().getAsInt();
        int[] counts = new int[maxValue + 1];

        // Phase 1: Erstelle das Histogramm
        for (int element : myArray) 
        {
            counts[element]++;
        }

        if (verbose)
        {
            System.out.println("Erstelle Histogramm als Array mit den Häufigkeiten der Zahlen im Array");
        
            // Optional - kann auskommentiert werden: Headline von Histogramm und Ausgabe Histogramm
            for (int i = 0; i < maxValue; i++)
            {
                System.out.print(i);

                if (i + 1 != maxValue)
                {
                    System.out.print(";");
                }
            }

            System.out.println("");
            this.OutputOfIntArray(counts);
        }

        // Phase 2: Aggregiere die Zahlen, um die Startadressen in Zielarray zu ermitteln
        for (int i = 1; i <= maxValue; i++) 
        {
            // Die Daten im Array werden von links nach rechts kummuliert
            // um die Startadresse für jede Zahl zu bekommen.
            counts[i] += counts[i - 1];
        }

        if (verbose)
        {
            System.out.println("Array mit den aggregierten Zahlen (Histogramm) für die Startadresse im Zielarray:");
            this.OutputOfIntArray(counts);
        }

        // Phase 3: Schreibe die Zahlen ins Zielarray
        int[] target = new int[myArray.length];

        for (int i = myArray.length - 1; i >= 0; i--) 
        {
            int element = myArray[i];
            
            counts[element] = counts[element] - 1;

            if (verbose)
            {
                System.out.println("Schreibe " + element +" an Index " + counts[element] + " des zu sortierten Arrays!");
            }

            target[counts[element]] = element;
        }

        // Copy target back to input array
        System.arraycopy(target, 0, myArray, 0, myArray.length);
    } 

    public static void main(String[] args) 
    {
        // Instanziere aus den statischem Programm ein echtes Objekt
        // damit nicht alle Methoden und Variablen static sein müssen.
        program = new CountingSort();
    }
}

Ausgabe

Kleines Array
=============
Vorher: 22;6;2;4;10;3;9;7;5;8;1
Nachher: 1;2;3;4;5;6;7;8;9;10;22
Ich habe 12449300 ns mit einem Array von 11 und einem Wertebereich/Histogramm von 22 Zahlen gebraucht! 


Großes Array, kleiner Wertebereich (Ausgabe aus)
================================================
Ich habe 75933500 ns mit einem Array von 10000000 und einem Wertebereich/Histogramm von 99999 Zahlen gebraucht! 


Kleines Array, großer Wertebereich (Ausgabe aus)
================================================
Ich habe 25634500 ns mit einem Array von 100000 und einem Wertebereich/Histogramm von 9999905 Zahlen gebraucht! 

Komplexität: O-Notation (Ordnung)

Die Laufzeit der Funktion hängt von {\displaystyle n} (Anzahl der Elemente des Eingabearrays) und {\displaystyle k} (die Größe des Zahlenintervalls) ab. Die for-Schleifen werden jeweils {\displaystyle n}-mal oder {\displaystyle k}-mal durchlaufen. Die Zeitkomplexität von Countingsort beträgt somit {\displaystyle {\mathcal {O}}(n+k)}.

Algorithmen und Datenstrukturen: Der optimierte Bubble Sort in Java

Der Algorithmus

Dieser Algorithmus ist eine Erweiterung des normalen Bubble Sort Algorithmus. Wie dieser wird hierbei ein Array durchlaufen und das Element der aktuellen Iteration mit dem Nachfolgeelement getauscht, wenn dieses kleiner ist. Dadurch blubbern die Zahlen vom Array-Anfang bis zum Ende in einen sortierten Bereich auf der rechten Seite des Arrays nach oben.

Der optimierte Bubble Sort-Algorithmus bricht weitere Iterationen ab, wenn er in in seiner if-Bedingung nichts mehr zum Tauschen gefunden hat. Dies vermerkt er in einer bool-Variable, was die umgebende do…while-Schleife nutzt um den Algorithmus abzubrechen.

package AlgoDat;
 
public class OptimizedBubbleSort {
    // Zu sortierendes Array
    private int myArray[] = {22, 6, 2, 4, 10, 3, 9, 7, 5, 8, 1};
 
    // Hält die Klasse als instanziertes Objekt
    @SuppressWarnings("unused")
    private static OptimizedBubbleSort program;
 
    // Hilfsfunktion für das Ausgeben des Arrays
    public void OutputOfIntArray(int myArray[])
    {
        if (myArray != null)
        {
            for (int i = 0; i < myArray.length; i++) {
                if (i > 0) System.out.print(";");
                System.out.print(myArray[i]);
            }
 
            System.out.println();
        }
    }
 
    // Konstruktor
    public OptimizedBubbleSort()
    {
        System.out.print("Vorher: ");
        this.OutputOfIntArray(myArray);
 
        // Da wir eine do .. while-Schleife nun nutzen,
        // zählen wir einen Index darin runter um diesen
        // im Array jederzeit adressieren zu können.
        int sortierterBereichRechts = myArray.length;
 
        // Wenn in einer Iteration nix getauscht wurde
        // wird das für alle weiteren auch der Fall sein.
        // In dem Fall kann der Algorithmus enden.
        boolean hatteNochWasZuTun = false;
 
        do
        {
            // Am Anfang gibts nix zu tun
            hatteNochWasZuTun = false;   
 
            System.out.println("Iteration: " + (myArray.length - sortierterBereichRechts + 1));
 
            for (int i = 0; i < sortierterBereichRechts - 1; i++)
            {
                if (myArray[i] > myArray[i + 1])
                {
                    this.vertausche(myArray, i, i + 1);
                    System.out.print("Tausche: ");
                    this.OutputOfIntArray(myArray);
                    hatteNochWasZuTun = true;
                }
            }
             
            // Der sortierte Bereich wächst
            sortierterBereichRechts--;
        }
        while (hatteNochWasZuTun);
 
        System.out.print("Nachher: ");
        this.OutputOfIntArray(myArray);
    }
 
    public void vertausche(int[] arrayToSwap, int idx1, int idx2)
    {
        int swapVar = arrayToSwap[idx1];
        arrayToSwap[idx1] = arrayToSwap[idx2];
        arrayToSwap[idx2] = swapVar;
    }
 
    public static void main(String[] args) 
    {
        // Instanziere aus den statischem Programm ein echtes Objekt
        // damit nicht alle Methoden und Variablen static sein müssen.
        program = new OptimizedBubbleSort();
    }
}

Ausgabe

Vorher: 22;6;2;4;10;3;9;7;5;8;1
Iteration: 1
Tausche: 6;22;2;4;10;3;9;7;5;8;1
Tausche: 6;2;22;4;10;3;9;7;5;8;1
Tausche: 6;2;4;22;10;3;9;7;5;8;1
Tausche: 6;2;4;10;22;3;9;7;5;8;1
Tausche: 6;2;4;10;3;22;9;7;5;8;1
Tausche: 6;2;4;10;3;9;22;7;5;8;1
Tausche: 6;2;4;10;3;9;7;22;5;8;1
Tausche: 6;2;4;10;3;9;7;5;22;8;1
Tausche: 6;2;4;10;3;9;7;5;8;22;1
Tausche: 6;2;4;10;3;9;7;5;8;1;22
Iteration: 2
Tausche: 2;6;4;10;3;9;7;5;8;1;22
Tausche: 2;4;6;10;3;9;7;5;8;1;22
Tausche: 2;4;6;3;10;9;7;5;8;1;22
Tausche: 2;4;6;3;9;10;7;5;8;1;22
Tausche: 2;4;6;3;9;7;10;5;8;1;22
Tausche: 2;4;6;3;9;7;5;10;8;1;22
Tausche: 2;4;6;3;9;7;5;8;10;1;22
Tausche: 2;4;6;3;9;7;5;8;1;10;22
Iteration: 3
Tausche: 2;4;3;6;9;7;5;8;1;10;22
Tausche: 2;4;3;6;7;9;5;8;1;10;22
Tausche: 2;4;3;6;7;5;9;8;1;10;22
Tausche: 2;4;3;6;7;5;8;9;1;10;22
Tausche: 2;4;3;6;7;5;8;1;9;10;22
Iteration: 4
Tausche: 2;3;4;6;7;5;8;1;9;10;22
Tausche: 2;3;4;6;5;7;8;1;9;10;22
Tausche: 2;3;4;6;5;7;1;8;9;10;22
Iteration: 5
Tausche: 2;3;4;5;6;7;1;8;9;10;22
Tausche: 2;3;4;5;6;1;7;8;9;10;22
Iteration: 6
Tausche: 2;3;4;5;1;6;7;8;9;10;22
Iteration: 7
Tausche: 2;3;4;1;5;6;7;8;9;10;22
Iteration: 8
Tausche: 2;3;1;4;5;6;7;8;9;10;22
Iteration: 9
Tausche: 2;1;3;4;5;6;7;8;9;10;22
Iteration: 10
Tausche: 1;2;3;4;5;6;7;8;9;10;22
Iteration: 11
Nachher: 1;2;3;4;5;6;7;8;9;10;22

Komplexität: O-Notation (Ordnung)

Worst- und Average-Case

Wie beim normalen Bubble Sort beträgt die Laufzeit-Komplexität im normalen und durchschnittlichen Fall O(n²).

Best-Case

Im Best-Case bricht der Algorithmus aber bereits nach einer Iteration ab, was einer Laufzeitkomplexität von O(n) entspricht.

Algorithmen und Datenstrukturen: Der Selection Sort in Java

Der Algorithmus

package AlgoDat;

public class SelectionSort {
    // Zu sortierendes Array
    private int myArray[] = {22, 6, 2, 4, 10, 3, 9, 7, 5, 8, 1};

    // Hält die Klasse als instanziertes Objekt
    @SuppressWarnings("unused")
    private static SelectionSort program;

    // Hilfsfunktion für das Ausgeben des Arrays
    public void OutputOfIntArray(int myArray[])
    {
        if (myArray != null)
        {
            for (int i = 0; i < myArray.length; i++) {
                if (i > 0) System.out.print(";");
                System.out.print(myArray[i]);
            }

            System.out.println();
        }
    }

    // Konstruktor
    public SelectionSort()
    {
        System.out.print("Vorher: ");
        this.OutputOfIntArray(myArray);

        // Laufe das zu sortierende Array von Anfang bis Ende durch
        for (int idxSortierterBereich = 0; idxSortierterBereich < myArray.length - 1 ; idxSortierterBereich++)
        {
            // Starte an der Index-Position der äußersten Schleife - davor ist schon alles sortiert
            int indexPivotElement = idxSortierterBereich;

            for (int idxUnsortierterBereich = idxSortierterBereich + 1; idxUnsortierterBereich < myArray.length; idxUnsortierterBereich++)
            {
                // ... und merke dir das kleinste Element
                if (myArray[indexPivotElement] > myArray[idxUnsortierterBereich])
                {
                    indexPivotElement = idxUnsortierterBereich;
                }
            }

            // Dieser Code tauscht das neu gefundene Minimum mit dem Element am aktuellen Index der äußeren Schleife                
            int swapVar = myArray[indexPivotElement];
            myArray[indexPivotElement] = myArray[idxSortierterBereich];
            myArray[idxSortierterBereich] = swapVar;

            System.out.print("Tausche: ");
            this.OutputOfIntArray(myArray);
        }

        System.out.print("Nachher: ");
        this.OutputOfIntArray(myArray);
    }

    public static void main(String[] args) 
    {
        // Instanziere aus den statischem Programm ein echtes Objekt
        // damit nicht alle Methoden und Variablen static sein müssen.
        program = new SelectionSort();
    }
}

Ausgabe

Vorher: 22;6;2;4;10;3;9;7;5;8;1
Tausche: 1;6;2;4;10;3;9;7;5;8;22
Tausche: 1;2;6;4;10;3;9;7;5;8;22
Tausche: 1;2;3;4;10;6;9;7;5;8;22
Tausche: 1;2;3;4;10;6;9;7;5;8;22
Tausche: 1;2;3;4;5;6;9;7;10;8;22
Tausche: 1;2;3;4;5;6;9;7;10;8;22
Tausche: 1;2;3;4;5;6;7;9;10;8;22
Tausche: 1;2;3;4;5;6;7;8;10;9;22
Tausche: 1;2;3;4;5;6;7;8;9;10;22
Tausche: 1;2;3;4;5;6;7;8;9;10;22
Nachher: 1;2;3;4;5;6;7;8;9;10;22

Komplexität: O-Notation (Ordnung)

Zwei verschaltete Schleifen.
Die äußere Schleife läuft von 1 bis n;
Die innere Schleife läuft vom Element der äußeren Schleife bis Schluss -> also n/2, da der Bereich immer kleiner wird.

O(T(n)) = O(n²)

Algorithmen und Datenstrukturen: Der Insertion Sort in Java

Der Algorithmus

Markant sind die zwei verschachtelten Schleifen, wobei die innere Schleife meistens eine While-Schleife mit 2 Bedingungen ist. Ein Index, welcher die Position der Trennung vom sortierten (links) und vom unsortierten (rechts) Bereich präsentiert, wird runtergezählt und das Array-Element an der Index-Position entspricht nach Ende der Schleife der Array-Position, mit der ein gemerktes Element getauscht werden kann. Während der sortierte Bereich (immer links) mit dem ersten Element des unsortierten Bereichs (immer rechts), welches sich gemerkt wird, verglichen wird, werden alle Elemente bis zu diesem Punkt um eins nach rechts gerückt. Dadurch existiert die zu tauschende Position nach diesem Schritt zwei Mal und wird durch das gemerkte Element ausgetauscht.

Die zweite Bedingung der inneren While-Schleife verhindert, das der runterzählende Index negativ wird.

package AlgoDat;

class InsertionSort {
    // Zu sortierendes Array
    private int myArray[] = {22, 6, 2, 4, 10, 3, 9, 7, 5, 8, 1};
    
    // Hält die Klasse InsertionSort als instanziertes Objekt
    @SuppressWarnings("unused")
    private static InsertionSort program;

    // Hilfsfunktion für das Ausgeben des Arrays
    public void OutputOfIntArray(int myArray[])
    {
        if (myArray != null)
        {
            for (int i = 0; i < myArray.length; i++) {
                if (i > 0) System.out.print(";");
                System.out.print(myArray[i]);
            }

            System.out.println();
        }
    }

    // Konstruktor
    public InsertionSort()
    {
        this.OutputOfIntArray(myArray);

        // Bei 1 beginnen, da das Element mit dem Index 0 bereits als sortiert gilt 
        for (int idxSortierterBereich = 1; idxSortierterBereich < myArray.length; idxSortierterBereich++)
        {
            // Merke dir das erste Element vom unsortierten Bereich
            int swapVar = myArray[idxSortierterBereich];
            System.out.println("Gemerkt vor dem Aufrücken: " + swapVar);

            // Das erste unsortierte Element auf der rechten Seite wird in den bereits sortierten Bereich 
            // auf der linken Seite eingefügt, womit der unsortierte Bereich immer weiter nach rechts rückt
            // und dann verschwindet.
            int idxUnsortierterBereich = idxSortierterBereich; 
            System.out.println("Der unsortierte Bereich beginnt bei Index: " + idxUnsortierterBereich);

            // Laufe im Array von rechts nach links, so lange wie vorige Element noch größer wie 
            // das erste Element vom unsortierten Bereich ist und der Bereich nicht negativ wird
            while (idxUnsortierterBereich > 0 && myArray[idxUnsortierterBereich - 1] > swapVar)
            {
                // Alles eins nach rechts im Array rücken bis zum bereits sortierten Bereich
                myArray[idxUnsortierterBereich] = myArray[idxUnsortierterBereich - 1] ;
                idxUnsortierterBereich--;

                System.out.print("Nach rechts aufrücken: ");
                this.OutputOfIntArray(myArray);
            }

            System.out.println("Tausche Stelle " + (idxUnsortierterBereich + 1) + " (" + myArray[idxUnsortierterBereich] + 
            ") mit gemerkter Stelle " + (idxSortierterBereich + 1) + " (" + swapVar + ")");

            myArray[idxUnsortierterBereich] = swapVar;

            System.out.print("Getauscht: ");
            this.OutputOfIntArray(myArray);
        }
    }

    public static void main(String[] args) 
    {
        // Instanziere aus den statischem Programm ein echtes Objekt
        // damit nicht alle Methoden und Variablen static sein müssen.
        program = new InsertionSort();
    }
}

Ausgabe

22;6;2;4;10;3;9;7;5;8;1
Gemerkt vor dem Aufrücken: 6
Der unsortierte Bereich beginnt bei Index: 1
Nach rechts aufrücken: 22;22;2;4;10;3;9;7;5;8;1
Tausche Stelle 1 (22) mit gemerkter Stelle 2 (6)
Getauscht: 6;22;2;4;10;3;9;7;5;8;1
Gemerkt vor dem Aufrücken: 2
Der unsortierte Bereich beginnt bei Index: 2
Nach rechts aufrücken: 6;22;22;4;10;3;9;7;5;8;1
Nach rechts aufrücken: 6;6;22;4;10;3;9;7;5;8;1
Tausche Stelle 1 (6) mit gemerkter Stelle 3 (2)
Getauscht: 2;6;22;4;10;3;9;7;5;8;1
Gemerkt vor dem Aufrücken: 4
Der unsortierte Bereich beginnt bei Index: 3
Nach rechts aufrücken: 2;6;22;22;10;3;9;7;5;8;1
Nach rechts aufrücken: 2;6;6;22;10;3;9;7;5;8;1
Tausche Stelle 2 (6) mit gemerkter Stelle 4 (4)
Getauscht: 2;4;6;22;10;3;9;7;5;8;1
Gemerkt vor dem Aufrücken: 10
Der unsortierte Bereich beginnt bei Index: 4
Nach rechts aufrücken: 2;4;6;22;22;3;9;7;5;8;1
Tausche Stelle 4 (22) mit gemerkter Stelle 5 (10)
Getauscht: 2;4;6;10;22;3;9;7;5;8;1
Gemerkt vor dem Aufrücken: 3
Der unsortierte Bereich beginnt bei Index: 5
Nach rechts aufrücken: 2;4;6;10;22;22;9;7;5;8;1
Nach rechts aufrücken: 2;4;6;10;10;22;9;7;5;8;1
Nach rechts aufrücken: 2;4;6;6;10;22;9;7;5;8;1
Nach rechts aufrücken: 2;4;4;6;10;22;9;7;5;8;1
Tausche Stelle 2 (4) mit gemerkter Stelle 6 (3)
Getauscht: 2;3;4;6;10;22;9;7;5;8;1
Gemerkt vor dem Aufrücken: 9
Der unsortierte Bereich beginnt bei Index: 6
Nach rechts aufrücken: 2;3;4;6;10;22;22;7;5;8;1
Nach rechts aufrücken: 2;3;4;6;10;10;22;7;5;8;1
Tausche Stelle 5 (10) mit gemerkter Stelle 7 (9)
Getauscht: 2;3;4;6;9;10;22;7;5;8;1
Gemerkt vor dem Aufrücken: 7
Der unsortierte Bereich beginnt bei Index: 7
Nach rechts aufrücken: 2;3;4;6;9;10;22;22;5;8;1
Nach rechts aufrücken: 2;3;4;6;9;10;10;22;5;8;1
Nach rechts aufrücken: 2;3;4;6;9;9;10;22;5;8;1
Tausche Stelle 5 (9) mit gemerkter Stelle 8 (7)
Getauscht: 2;3;4;6;7;9;10;22;5;8;1
Gemerkt vor dem Aufrücken: 5
Der unsortierte Bereich beginnt bei Index: 8
Nach rechts aufrücken: 2;3;4;6;7;9;10;22;22;8;1
Nach rechts aufrücken: 2;3;4;6;7;9;10;10;22;8;1
Nach rechts aufrücken: 2;3;4;6;7;9;9;10;22;8;1
Nach rechts aufrücken: 2;3;4;6;7;7;9;10;22;8;1
Nach rechts aufrücken: 2;3;4;6;6;7;9;10;22;8;1
Tausche Stelle 4 (6) mit gemerkter Stelle 9 (5)
Getauscht: 2;3;4;5;6;7;9;10;22;8;1
Gemerkt vor dem Aufrücken: 8
Der unsortierte Bereich beginnt bei Index: 9
Nach rechts aufrücken: 2;3;4;5;6;7;9;10;22;22;1
Nach rechts aufrücken: 2;3;4;5;6;7;9;10;10;22;1
Nach rechts aufrücken: 2;3;4;5;6;7;9;9;10;22;1
Tausche Stelle 7 (9) mit gemerkter Stelle 10 (8)
Getauscht: 2;3;4;5;6;7;8;9;10;22;1
Gemerkt vor dem Aufrücken: 1
Der unsortierte Bereich beginnt bei Index: 10
Nach rechts aufrücken: 2;3;4;5;6;7;8;9;10;22;22
Nach rechts aufrücken: 2;3;4;5;6;7;8;9;10;10;22
Nach rechts aufrücken: 2;3;4;5;6;7;8;9;9;10;22
Nach rechts aufrücken: 2;3;4;5;6;7;8;8;9;10;22
Nach rechts aufrücken: 2;3;4;5;6;7;7;8;9;10;22
Nach rechts aufrücken: 2;3;4;5;6;6;7;8;9;10;22
Nach rechts aufrücken: 2;3;4;5;5;6;7;8;9;10;22
Nach rechts aufrücken: 2;3;4;4;5;6;7;8;9;10;22
Nach rechts aufrücken: 2;3;3;4;5;6;7;8;9;10;22
Nach rechts aufrücken: 2;2;3;4;5;6;7;8;9;10;22
Tausche Stelle 1 (2) mit gemerkter Stelle 11 (1)
Getauscht: 1;2;3;4;5;6;7;8;9;10;22

Komplexität: O-Notation (Ordnung)

O(T(n)) = O(n^2/2+n/2-n) = O(n^2/2) = O (n^2)

Die äußere Schleife läuft von 1 bis n-1, während die innere While-Schleife vom ersten Element des unsortierten Bereichs bis zu der Stelle der richtige Einfügeposition läuft.

Äußere Schleife: Iteriert n-1 mal.
Innere Schleife: Iteriert 1x für Element 1, 2x für Element 2, 3x für Element 3, … n mal für Element n, was zu einer Laufzeit von

führt. Daraus folgt:

Additive Bestandteile, Faktoren und Konstanten fallen bei der Bestimmung der Ordnung weg, daher ist die Ordnung O(n²). Die Domäne ist der dominante Teil der Ordnung – sie ist n² .