淺談排序算法之插入排序(3)

筆者在前面兩篇文章當中簡單介紹了下冒泡排序以及選擇排序,這裏順帶介紹下插入排序(Insertion-Sort)。

舉個栗子(出自《算法導論》第三版)。

一堆無序的撲克牌,從上面抽取第一張,放在手上,然後抽取第二張,比較和第一張的大小。若小於第一張撲克,則放在第一張撲克的前面,否則放在第一張撲克的後面。然後抽取第三張,若小於第一張撲克,在放在最前面;若大於等於第一張撲克且小於等於第二張撲克,則放在兩張撲克中間,否則放在第二張撲克後面…以此類推,可以得到一個升序排序的撲克牌序列。

上述過程闡明瞭插入排序的基本原理。

  • 算法從數組的第二個元素開始.
  • 每次迭代中,都會將當前位置元素的值與前面元素的值進行比較。若前面的某一元素的值大於當前位置元素的值,則後移之,直到插入點前面的元素都小於當前元素的值,後面的元素都大於等於當前元素的值。

最壞情況下,插入排序的時間複雜度大致爲O(n^2)。由於在插入排序中,通常情況下只有當前面的元素大於當前元素的值的時候,前面的元素值在往後移動。因此,插入排序是一種穩定的排序算法。另外,由於在《算法導論》看到過一個提示,大概的意思是

如果要對數組A[1,2,…n]進行排序,需首先對數組A[1,2,…n-1]排序。要對數組A[1,2,…n-1]進行排序,則需要首先對數組A[1,2,…n-2]進行排序…因此,考慮將插入排序的過程採用遞歸(Recursion)來實現。

插入排序-for循環版本

import com.sun.istack.internal.NotNull;

import java.util.Arrays;
import java.util.Random;

/**
 * A demo of {@code InsertionSort}.
 *
 * @author Mr.K
 */
public class InsertionSort {

    public static void main(String[] args) {
        int N = 20;
        int[] numbers = new int[N];
        Random random = new Random();
        for (int i = 0; i < N; i++) {
            numbers[i] = random.nextInt(2 * N);
        }
        System.out.println(Arrays.toString(numbers) + "\n");
        insertionSort(numbers);
        System.out.println("\n" + Arrays.toString(numbers));
    }

    /**
     * Accepts an array, each element is an integer number and sorts the array by algorithm
     * {@code InsertionCode}, which starts from the second element, and ends at the last one.
     * <ul>
     *     <li>For each iteration, current element compares with elements ahead.</li>
     *     <li>if a certain element is greater than current element, that certain element will
     *     move to the next element until there is no element is greater than current element,
     *     in the form of key</li>
     *     <li>At last, current element will be placed in the right place where each element
     *     ahead is less than or equals to current element, and elements backward is greater
     *     than current element.</li>
     * </ul>
     * Since the parameter is in a form of array, which means it's a reference, thus no results
     * will be returned.<br><br>
     * <p>
     * Be aware that, in the bad cases, the cost of time of {@code InsertionSort} is O(n^2),
     * which may be a bottleneck for large number of numbers to be sorted.<br><br>
     * By the way, {@code InsertionSort} is stable cause the judgement is
     * <blockquote>
     *     numbers[i] > key
     * </blockquote>
     * If there are some numbers with the same values, only when the number in the sorted
     * sequence if greater than the sorting number, exchange will be made. If the judgement
     * changes to
     * <blockquote>
     *     numbers[i] >= key
     * </blockquote>
     * Then, if two number has the same value, they will exchange their position, or index if you
     * like. So the order of both number changes, which is different from the original array. In
     * that case, {@code InsertionSort} is unstable.
     *
     * @param numbers numbers to be sorted, in a form array
     */
    public static void insertionSort(@NotNull int[] numbers) {
        for (int j = 1; j < numbers.length; j++) {
            int key = numbers[j];
            int i = j - 1;
            System.out.println("第" + String.format("%2d", j) + "步, 待排序數字: " +
                    String.format("%2d", key) + " -> " + Arrays.toString(numbers));
            while (i >= 0 && numbers[i] > key) {
                numbers[i + 1] = numbers[i];
                i--;
            }
            numbers[i + 1] = key;
        }
    }

}

插入排序-遞歸版本

import com.sun.istack.internal.NotNull;

import java.util.Arrays;
import java.util.Random;

/**
 * A demo of {@code InsertionSort} by invoking {@code InsertionSort} itself.
 *
 * @author Mr.K
 */
public class InsertionSortByRecursion {


    public static void main(String[] args) {
        int N = 20;
        int[] numbers = new int[N];
        Random random = new Random();
        for (int i = 0; i < numbers.length; i++) {
            numbers[i] = random.nextInt(2 * N);
        }
        System.out.println("待排序數組: " + Arrays.toString(numbers) + "\n");
        insertionSortByRecursion(numbers, numbers.length - 1);
        System.out.println("\n已排序數組: " + Arrays.toString(numbers));
    }

    /**
     * Accepts an array and an index and sorts this array by invoking itself, which is so
     * called {@code Recursion}. The index is the condition to terminate the {@code Recursion}.
     * When the index goes to 0, which means it's the first element of the specified array,
     * the process goes back and starts the process of {@code InsertionSort}.
     * <ul>
     *     <li>When this method is invoked, it will check that whether the index equals to 0.
     *     If so, then returns and sort the array, in a range of [0, 1] with {@code InsertionSort}.
     *     And then, sub-array in a range of [0, 2] should be sorted as well until the whole
     *     array is sorted.</li>
     * </ul>
     * This method is stable cause only when a certain is greater than current key number, then that
     * number will be moved backwards by one step.<br><br>
     * The cost of the time of {@code InsertionSort} is O(n^2) in a bad case, which has no differences
     * from the version, who uses <em>For-Loop</em> to finish the iterations.
     *
     * @param numbers specified array to be sorted
     * @param index   index of the end of current range, which start from 0(inclusive)
     */
    public static void insertionSortByRecursion(@NotNull int[] numbers, @NotNull int index) {
        if (index == 0) {
            return;
        } else {
            insertionSortByRecursion(numbers, index - 1);
            int i = index - 1, key = numbers[index];
            System.out.println("第" + String.format("%2d", index) + "步, 待排序數字: " +
                    String.format("%2d", key) + " -> " + Arrays.toString(numbers));
            while (i >= 0 && numbers[i] > key) {
                numbers[i + 1] = numbers[i--];
            }
            numbers[i + 1] = key;
        }
    }

}

程序運行結果如下所示

待排序數組: [38, 18, 23, 38, 12, 18, 32, 27, 6, 7, 0, 1, 38, 2, 34, 15, 9, 0, 5, 27]1, 待排序數字: 18 -> [38, 18, 23, 38, 12, 18, 32, 27, 6, 7, 0, 1, 38, 2, 34, 15, 9, 0, 5, 27]2, 待排序數字: 23 -> [18, 38, 23, 38, 12, 18, 32, 27, 6, 7, 0, 1, 38, 2, 34, 15, 9, 0, 5, 27]3, 待排序數字: 38 -> [18, 23, 38, 38, 12, 18, 32, 27, 6, 7, 0, 1, 38, 2, 34, 15, 9, 0, 5, 27]4, 待排序數字: 12 -> [18, 23, 38, 38, 12, 18, 32, 27, 6, 7, 0, 1, 38, 2, 34, 15, 9, 0, 5, 27]5, 待排序數字: 18 -> [12, 18, 23, 38, 38, 18, 32, 27, 6, 7, 0, 1, 38, 2, 34, 15, 9, 0, 5, 27]6, 待排序數字: 32 -> [12, 18, 18, 23, 38, 38, 32, 27, 6, 7, 0, 1, 38, 2, 34, 15, 9, 0, 5, 27]7, 待排序數字: 27 -> [12, 18, 18, 23, 32, 38, 38, 27, 6, 7, 0, 1, 38, 2, 34, 15, 9, 0, 5, 27]8, 待排序數字:  6 -> [12, 18, 18, 23, 27, 32, 38, 38, 6, 7, 0, 1, 38, 2, 34, 15, 9, 0, 5, 27]9, 待排序數字:  7 -> [6, 12, 18, 18, 23, 27, 32, 38, 38, 7, 0, 1, 38, 2, 34, 15, 9, 0, 5, 27]10, 待排序數字:  0 -> [6, 7, 12, 18, 18, 23, 27, 32, 38, 38, 0, 1, 38, 2, 34, 15, 9, 0, 5, 27]11, 待排序數字:  1 -> [0, 6, 7, 12, 18, 18, 23, 27, 32, 38, 38, 1, 38, 2, 34, 15, 9, 0, 5, 27]12, 待排序數字: 38 -> [0, 1, 6, 7, 12, 18, 18, 23, 27, 32, 38, 38, 38, 2, 34, 15, 9, 0, 5, 27]13, 待排序數字:  2 -> [0, 1, 6, 7, 12, 18, 18, 23, 27, 32, 38, 38, 38, 2, 34, 15, 9, 0, 5, 27]14, 待排序數字: 34 -> [0, 1, 2, 6, 7, 12, 18, 18, 23, 27, 32, 38, 38, 38, 34, 15, 9, 0, 5, 27]15, 待排序數字: 15 -> [0, 1, 2, 6, 7, 12, 18, 18, 23, 27, 32, 34, 38, 38, 38, 15, 9, 0, 5, 27]16, 待排序數字:  9 -> [0, 1, 2, 6, 7, 12, 15, 18, 18, 23, 27, 32, 34, 38, 38, 38, 9, 0, 5, 27]17, 待排序數字:  0 -> [0, 1, 2, 6, 7, 9, 12, 15, 18, 18, 23, 27, 32, 34, 38, 38, 38, 0, 5, 27]18, 待排序數字:  5 -> [0, 0, 1, 2, 6, 7, 9, 12, 15, 18, 18, 23, 27, 32, 34, 38, 38, 38, 5, 27]19, 待排序數字: 27 -> [0, 0, 1, 2, 5, 6, 7, 9, 12, 15, 18, 18, 23, 27, 32, 34, 38, 38, 38, 27]

已排序數組: [0, 0, 1, 2, 5, 6, 7, 9, 12, 15, 18, 18, 23, 27, 27, 32, 34, 38, 38, 38]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章