Javaにおける挿入ソート

Javaでの挿入ソート

1. 概要

このチュートリアルでは、the Insertion Sort algorithm and have a look at its Java implementationについて説明します。

挿入ソートは、少数のアイテムを注文するための効率的なアルゴリズムです。 この方法は、カードプレーヤーがトランプの手を分類する方法に基づいています。

空の左手から始めて、カードをテーブルに置きます。 次に、テーブルからカードを1枚ずつ取り出して、左手の正しい位置に挿入します。 新しいカードの正しい位置を見つけるために、既にソートされているカードのセットと右から左に比較します。

擬似コード形式のアルゴリズムステップを理解することから始めましょう。

2. 疑似コード

挿入ソートの擬似コードをINSERTION-SORTというプロシージャとして提示し、ソートするn個のアイテムの配列A[1 .. 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は、処理する配列内の現在のアイテムの位置を示します。

定義により、1つのアイテムを持つ配列はソートされていると見なされるため、2番目のアイテムから開始します。 インデックスiのアイテムは、keyと呼ばれます。 key,を取得すると、アルゴリズムの2番目の部分は正しいインデックスの検索を処理します。 If the key is smaller than the value of the item at index j, then the key moves one position to the left.このプロセスは、キーよりも小さい要素に到達するまで続きます。

インデックスikeyの正しい位置を見つけるための反復を開始する前に、配列A[1 .. j – 1]はすでにsortedであることに注意することが重要です。

3. 命令的な実装

命令型の場合、整数の配列をパラメーターとして使用して、insertionSortImperativeという関数を記述します。 関数は、2番目の項目から配列を反復処理し始めます。

反復中の任意の時点で、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.この場合、何もしません。

後続のすべての再帰呼び出しは、入力配列の事前定義部分をソートします。2番目の項目から開始して、配列の最後に到達します。

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)。 新しい項目ごとに、配列の既にソートされた部分を右から左に繰り返して、正しい位置を見つけます。 次に、アイテムを1つ右にシフトして挿入します。

アルゴリズムはその場でソートされるため、その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で見つかりました。