排序算法算法之Insertion Sort

来源:互联网 发布:中文域名转码器 编辑:程序博客网 时间:2024/05/22 15:02

引言

感觉1年前写的这篇文章有些不全面,因此今天我想完善一下这篇文章。

插入排序的几大优势

毋庸置疑,Insertion Sort 与其它几个高级排序算法(quicksort, heapsort, merge sort)相比,效率要低得多。但是,它也有自己的几大优势:

  • 实现简单
  • 对于小数据集很有效
  • 在实际应用中,它要比其它的一些 O(n2) 算法(比如:bubble sort)更有效
  • Adaptive - 即它可以利用有序元素的优势
  • 稳定的,即相等元素的相对顺序不变
  • 原址的

实现插入排序

下图是我从 wikipedia 上找到的,它很清楚地演示了插入排序的过程。

Insertion Sort

我相信大家看过上图以后,应该很清楚排序地整个过程了吧。其实它的整个排序过程像大家玩扑克抓牌的整个过程,你左手里面的牌都是有序的,当你从桌子上抓新牌的时候,你拿它与你左手上的牌逐个进行比较,然后找到一个合适的位置并插入进去。

下面我用 Java 来实现一下 Insertion Sort.

public class InsertionSort {    public static void main(String[] args) {        int[] a = {5, 2, 4, 6, 1, 3};        sort(a);        System.out.println("haha: " + Arrays.toString(a));    }    private static void sort(int[] arr) {        for (int j = 1; j < arr.length; j++) {            int key = arr[j];            int i = j - 1;            while (i >= 0 && key < arr[i]) {                arr[i + 1] = arr[i];                i--;            }            arr[i + 1] = key;        }    }}

接下来,我用循环不变式来证明一下上面算法的正确性。

  1. Loop Invariant : arr[0 … j - 1] 是有序的
  2. Initialization : 由于从 j = 1 开始,上面循环不变式的数组中只有1个元素,这肯定是有序的
  3. Maintenance : for 循环内不断地把 arr[j - 1],arr[j - 2],arr[j - 3] 等向右移动,直到为 arr[j] ,即上述代码中的 key 变量,找到一个合适的位置插入进去。因此在下轮迭代前,即把 j 变量增加1之前,arr[0, j] 是有序的,当下一轮迭代开始时,即 j 变量被加1以后,arr[0, j - 1] 依然保持有序
  4. Termination : 变量 j 的最终值将等于 arr.length ,由于我们已经说明了上面的循环不变式在每次迭代中都保持不变,因此,arr[0, j - 1] ,即arr[0, arr.length - 1] 最终将是有序的,即整个数组都是有序的

分析时间复杂度

从上面的代码可以看出,如果待排序的数组已经有序了,每次 for 循环的迭代中,都不会去进入 while 循环内执行,因此最好情况的时间复杂度为 O(n).

最坏情况就是如果待排序的数组是逆序的,它的时间复杂度为 O(n2).

平均时间复杂度也是 O(n2) , 因此 Insertion Sort 不适合排序大的数组。然而,当它用于很小的数组时,它是最快的算法之一,甚至要比 quicksort 还要快; 实际上,一个实现好的 quicksort ,当要排序的元素个数小于某个阙值,算法会自动切换到 Insertion Sort 来排序; 具体的阙值应该去实验,并且也取决于机器,通常情况下,大约是10左右。

通过这个时间复杂度的分析,我相信大家对我上面所说的 Insertion Sort 具有 Adaptive 的性质,会有一个更深地理解。

0 0
原创粉丝点击