浅谈排序算法之插入排序(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]
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值