Tri par insertion en Java

Tri par insertion en Java

1. Vue d'ensemble

Dans ce didacticiel, nous allons discuter dethe Insertion Sort algorithm and have a look at its Java implementation.

Le tri par insertion est un algorithme efficace pour commander un petit nombre d'éléments. Cette méthode est basée sur la façon dont les joueurs de cartes trient une main de cartes à jouer.

Nous commençons avec une main gauche vide et les cartes posées sur la table. Nous retirons ensuite une carte à la fois de la table et l’insérons à la position correcte dans la main gauche. Pour trouver la bonne position pour une nouvelle carte, nous la comparons au jeu de cartes déjà trié dans la main, de droite à gauche.

Commençons par comprendre les étapes de l'algorithme sous forme de pseudocode.

2. Pseudocode

Nous allons présenter notre pseudocode pour le tri par insertion comme une procédure appeléeINSERTION-SORT, prenant comme paramètre un tableauA[1 .. n] de n éléments à trier. The algorithm sorts the input array in-place  (en réorganisant les éléments dans le tableau A).

Une fois la procédure terminée, le tableau d'entrée A contient une permutation de la séquence d'entrée, mais dans un ordre trié:

INSERTION-SORT(A)

for i=2 to A.length
    key = A[i]
    j = i - 1
    while j > 0 and A[j] > key
        A[j+1] = A[j]
        j = j - 1
    A[j + 1] = key

Passons brièvement en revue l'algorithme ci-dessus.

L'indexi indique la position de l'élément courant dans le tableau à traiter.

Nous commençons par le deuxième élément car, par définition, un tableau avec un élément est considéré comme trié. L'élément à l'indexi est appelé unkey. Une fois que vous avez leskey,, la deuxième partie de l'algorithme consiste à trouver son index correct. If the key is smaller than the value of the item at index j, then the key moves one position to the left. Le processus se poursuit jusqu'au cas où nous atteignons un élément plus petit que la clé.

Il est important de noter qu'avant de commencer l'itération pour trouver la position correcte deskey à l'indexi, le tableauA[1 .. j – 1] est déjàsorted.

3. Mise en œuvre impérative

Pour le cas impératif, nous allons écrire une fonction appeléeinsertionSortImperative, en prenant comme paramètre un tableau d'entiers. La fonction commence à parcourir le tableau à partir du deuxième élément.

A tout moment de l'itération,we could think of this array as being logically divided into two portions; le côté gauche étant celui trié et le côté droit contenant les éléments non encore triés.

Une note importante ici est qu'après avoir trouvé la position correcte à laquelle nous allons insérer le nouvel élément,we shift (and not swap) the items to the right pour libérer un espace pour celui-ci.

public static void insertionSortImperative(int[] input) {
    for (int i = 1; i < input.length; i++) {
        int key = input[i];
        int j = i - 1;
        while (j >= 0 && input[j] > key) {
            input[j + 1] = input[j];
            j = j - 1;
        }
        input[j + 1] = key;
    }
}

Ensuite, créons un test pour la méthode ci-dessus:

@Test
public void givenUnsortedArray_whenInsertionSortImperative_thenSortedAsc() {
    int[] input = {6, 2, 3, 4, 5, 1};
    InsertionSort.insertionSortImperative(input);
    int[] expected = {1, 2, 3, 4, 5, 6};
    assertArrayEquals("the two arrays are not equal", expected, input);
}

Le test ci-dessus prouve que l'algorithme trie correctement dans l'ordre croissant le tableau d'entrée<6, 2, 3, 4, 5, 1>.

4. Implantation récursive

La fonction pour le cas récursif est appeléeinsertionSortRecursive and accepte en entrée un tableau d'entiers (comme pour le cas impératif).

La différence ici avec le cas impératif (malgré le fait qu'il soit récursif) est qu'ilcalls an overloaded function with a second argument that equals the number of items to sort.

Comme nous voulons trier le tableau complet, nous passerons un nombre d'éléments égal à sa longueur:

public static void insertionSortRecursive(int[] input) {
    insertionSortRecursive(input, input.length);
}

Le cas récursif est un peu plus difficile. The base case occurs when we attempt to sort an array with one item. Dans ce cas, nous ne faisons rien.

Tous les appels récursifs suivants trient une partie prédéfinie du tableau en entrée - à partir du deuxième élément jusqu'à la fin du tableau:

private static void insertionSortRecursive(int[] input, int i) {
    if (i <= 1) {
        return;
    }
    insertionSortRecursive(input, i - 1);
    int key = input[i - 1];
    int j = i - 2;
    while (j >= 0 && input[j] > key) {
        input[j + 1] = input[j];
        j = j - 1;
    }
    input[j + 1] = key;
}

Et voici à quoi ressemble la pile d'appels pour un tableau d'entrée de 6 éléments:

insertionSortRecursive(input, 6)
insertionSortRecursive(input, 5) and insert the 6th item into the sorted array
insertionSortRecursive(input, 4) and insert the 5th item into the sorted array
insertionSortRecursive(input, 3) and insert the 4th item into the sorted array
insertionSortRecursive(input, 2) and insert the 3rd item into the sorted array
insertionSortRecursive(input, 1) and insert the 2nd item into the sorted array

Voyons également le test pour cela:

@Test
public void givenUnsortedArray_whenInsertionSortRecursively_thenSortedAsc() {
    int[] input = {6, 4, 5, 2, 3, 1};
    InsertionSort.insertionSortRecursive(input);
    int[] expected = {1, 2, 3, 4, 5, 6};
    assertArrayEquals("the two arrays are not equal", expected, input);
}

Le test ci-dessus prouve que l'algorithme trie correctement dans l'ordre croissant le tableau d'entrée<6, 2, 3, 4, 5, 1>.

5. Complexité Temps et Espace

The time taken by the INSERTION-SORT procedure to run is O(n^2). Pour chaque nouvel élément, nous parcourons la partie déjà triée du tableau de droite à gauche pour trouver sa position correcte. Ensuite, nous l'insérons en déplaçant les éléments d'une position vers la droite.

L'algorithme trie sur place donc sesspace complexity is O(1) for the imperative implementation and O(n) for the recursive implementation.

6. Conclusion

Dans ce tutoriel, nous avons vu comment implémenter le tri par insertion.

Cet algorithme est utile pour trier un petit nombre d'éléments. It becomes inefficient when sorting input sequences having more than 100 items. 

Gardez à l'esprit que malgré sa complexité quadratique, il trie sur place sans avoir besoin d'espace auxiliaire comme c'est le cas pourmerge sort.

Le code entier a pu être trouvéover on GitHub.