Сортировка вставок в Java

Сортировка вставок в Java

1. обзор

В этом руководстве мы собираемся обсудитьthe Insertion Sort algorithm and have a look at its Java implementation.

Insertion Sort - эффективный алгоритм заказа небольшого количества товаров. Этот метод основан на том, как игроки в карты сортируют руки по игральным картам.

Начнем с пустой левой руки и карт, положенных на стол. Затем мы убираем одну карточку за раз со стола и вставляем ее в правильное положение в левой руке. Чтобы найти правильную позицию для новой карты, мы сравниваем ее с уже отсортированным набором карт в руке справа налево.

Давайте начнем с понимания шагов алгоритма в форме псевдокода.

2. ПСЕВДОКОД

Мы собираемся представить наш псевдокод для сортировки вставкой в ​​виде процедуры под названиемINSERTION-SORT, принимающей в качестве параметра массивA[1 .. n] из n элементов для сортировки. The algorithm sorts the input array in-place  (переставляя элементы в массиве A).

После завершения процедуры входной массив A содержит перестановку входной последовательности, но в отсортированном порядке:

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

Давайте кратко рассмотрим приведенный выше алгоритм.

Индексi указывает позицию текущего элемента в массиве для обработки.

Мы начинаем со второго элемента, так как по определению массив с одним элементом считается отсортированным. Элемент с индексомi называетсяkey. После полученияkey, вторая часть алгоритма занимается поиском правильного индекса. If the key is smaller than the value of the item at index j, then the key moves one position to the left. Процесс продолжается до тех пор, пока мы не дойдем до элемента, который меньше ключа.

Важно отметить, что перед началом итерации для поиска правильной позицииkey в индексеi массивA[1 .. j – 1] уже имеет значениеsorted.

3. Императивная реализация

В императивном случае мы собираемся написать функцию с именемinsertionSortImperative, принимающую в качестве параметра массив целых чисел. Функция начинает перебирать массив со второго элемента.

В любой момент времени во время итерацииwe could think of this array as being logically divided into two portions; левая сторона является отсортированной, а правая сторона содержит еще не отсортированные элементы.

Здесь важно отметить, что после нахождения правильной позиции, в которую мы вставим новый элемент,we shift (and not swap) the items to the right, чтобы освободить для него место.

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;
    }
}

Затем давайте создадим тест для описанного выше метода:

@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);
}

Приведенный выше тест доказывает, что алгоритм правильно сортирует входной массив<6, 2, 3, 4, 5, 1> в порядке возрастания.

4. Рекурсивная реализация

Функция для рекурсивного случая называетсяinsertionSortRecursive и принимает на вход массив целых чисел (как и для императивного случая).

Здесь отличие от императивного случая (несмотря на то, что он рекурсивен) в том, что онcalls an overloaded function with a second argument that equals the number of items to sort.

Поскольку мы хотим отсортировать весь массив, мы передадим количество элементов, равное его длине:

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

Рекурсивный случай немного сложнее. The base case occurs when we attempt to sort an array with one item. В этом случае мы ничего не делаем.

Все последующие рекурсивные вызовы сортируют предопределенную часть входного массива - начиная со второго элемента до достижения конца массива:

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;
}

Вот как выглядит стек вызовов для входного массива из 6 элементов:

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

Давайте также посмотрим на это:

@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);
}

Приведенный выше тест доказывает, что алгоритм правильно сортирует входной массив<6, 2, 3, 4, 5, 1> в порядке возрастания.

5. Время и пространство сложность

The time taken by the INSERTION-SORT procedure to run is O(n^2). Для каждого нового элемента мы выполняем итерацию справа налево по уже отсортированной части массива, чтобы найти правильную позицию. Затем мы вставляем его, сдвигая элементы на одну позицию вправо.

Алгоритм сортируется по месту, поэтомуspace complexity is O(1) for the imperative implementation and O(n) for the recursive implementation.

6. Заключение

В этом уроке мы увидели, как реализовать сортировку вставками.

Этот алгоритм полезен для сортировки небольшого количества предметов. It becomes inefficient when sorting input sequences having more than 100 items. с

Имейте в виду, что, несмотря на квадратичную сложность, он сортируется на месте без необходимости во вспомогательном пространстве, как в случаеmerge sort.

Весь код можно найтиover on GitHub.