数据结构—插入排序及其优化分析

基本思想


每一步将待排序的元素,按照其排序码的大小,插入到前面已经排好序的一组元素的合适位置上去,直到元素全部插完为止(可以想象打牌的场景,每拿到一张牌,就要找到合适的位置,然后把牌插进去)。

具体算法实现


假设现有 n 个待排序的元素,对应的关键字分别为 a1、a2、……..an,因为第一个元素是有序的,所以从第二个元素开始,将 a2 与 a1 进行比较。若 a2 < a1,则将 a2 插入到 a1 之前;否则,说明已经有序,不需要移动 a2。

现在有序的元素变为2个,再从第三个元素 a3 开始,先和 a2 进行比较,若 a3小于a2,再将 a3 和 a1 进行比较,若 a3小于a1,则将 a3 插入到 a1 的前面;否则,说明已经有序,继续插入下一个元素。

但是,这个算法还存在一点bug,如果 a3大于a1 呢?这时我们就会想到搬移元素了,即先将要进行插入的元素附给变量 key ,用 key 和已经排好序的元素依次进行比较;像上面的问题,就可以先让 a2 搬移到 key 的位置,再将 key 插入。
这里写图片描述

(图画的比较丑,多多包涵啦)按照上述方法,直到最后一个元素插入完成。(整个过程可以想象成打牌,每拿到一张新牌,找到它要放的位置后,肯定要将原有的往后移,才有位置插入)。

代码实现


void InsertSort(int array[], int size)
{
    int i = 1;
    for (; i < size; ++i)
    {
        int key = array[i];
        int end = i - 1;
        while (end >= 0 && key < array[end])
        {
            //搬移元素
            array[end + 1] = array[end];
            --end;
        }
        array[end + 1] = key;
    }
}

根据以上算法及代码可以发现一个问题,每次都要一个一个去比较,效率太低,有没有什么方法可以减少比较的次数呢?
还记得二分查找吧,这里,我们就可以使用二分查找的算法思想寻找插入位置,我们将这种插入排序称为折半插入排序。

折半插入排序


void InsertSort_OP(int array[], int size)
{
    int i = 1;
    for (; i < size; ++i)
    {
        int key = array[i];
        int begin = 0;
        int end = i - 1;
        int mid = 0;
        while (begin <= end)
        {
            int mid = (begin + end) / 2;
            if (array[mid] < key)
            {
                begin = mid + 1;
            }
            else
            {
                end = mid - 1;
            }
        }
        mid = i - 1;
        while (mid >= end && key < array[mid])
        {
            //搬移元素
            array[mid + 1] = array[mid];
            --mid;
        }
        array[mid + 1] = key;
    }
}

总结


  1. 稳定度
    不论是直接插入排序还是折半插入排序,都是稳定的。
    2.时间复杂度
    直接插入排序中,最好的情况,即所有元素的关键字都已经有序,此时外层的for循环执行 n-1 次,而内层的 while 循环不执行,时间复杂度为 O(n);在最坏的情况下,内外层循环都执行 n 次,时间复杂度为 O(n^2)。
    折半插入排序中,平均时间复杂度为 O(nlog2(n))。
    3.空间复杂度
    因为直接插入排序和折半插入排序,都没有借助辅助空间,所以空间复杂度都为 O(1)。

猜你喜欢

转载自blog.csdn.net/wwt_lb1314/article/details/80453447