排序——插入排序(Insertion sort)

12 篇文章 26 订阅

算法思想

顾名思义,采用插入的方式,对无序数列进行排序。

维护一个有序区,将数据一个一个插入到有序区的适当位置,直到整个数组都有序即每步将一个待排序的记录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。

图示过程

初始状态

数列的初始值为

先吧首元素 5 作为有序区,此时有序区只有一个元素,如下图所示。

第一轮

将下一个元素 8 和有序区所有元素依次比较,找到合适的位置,然后插入。

8> 5,所以元素 8 和元素 5 不需要交换。这样有序区的元素增加到两个,如下图所示。

第二轮

将下一个元素 6 和有序区所有元素依次比较,找到合适的位置,然后插入。

6< 8,所以元素 6 和元素 8 进行交换,如下图所示。

再比较 6 和 5 ,6>5,所以元素 6 和元素 5 不需要交换。这样有序区的元素增加到三个,如下图所示。

第三轮

将下一个元素 3 和有序区所有元素依次比较,找到合适的位置,然后插入。

3< 8,所以元素 3 和元素 8 进行交换,如下图所示。

3< 6,所以元素 3 和元素 6 进行交换,如下图所示。

3< 5,所以元素 3 和元素 5 进行交换,如下图所示。

这样有序区的元素增加到四个,如下图所示。

......

依次类推,直到整个数列的元素插入完毕,排序完成。

整个插入排序过程

下图是每一轮插入排序的结果。

说明:上述的排序过程没有记性优化,相关的优化可以参考下面的内容。

算法优化

注意:插入排序的算法优化不会降低算法的时间复杂度,只是减少了许多无谓的交换

我们观察一下第三轮操作中,发现需要让元素 3 逐个与有序区的元素进行比较和交换,整个交换过程是:与 8 交换、与 6 交换、与 5 交换,最终交换到有序区的第一个位置。可以发现,并不需要进行着三次交换,算法可以进行优化。

只需要把元素 3 暂时保存起来,先把有序区的元素从左到右逐一复制,再将元素 3 插入到合适的位置即可。具体过程用图示说明。

第一步

暂存元素 3 ,入下图所示。

第二步

和前一个元素比较,由于 3 < 8,复制元素 8 到它下一个位置。如下图所示。

第三步

和前一个元素比较,由于 3 < 6,复制元素 6 到它下一个位置。如下图所示。

第四步

和前一个元素比较,由于 3 < 5,复制元素 5 到它下一个位置。如下图所示。

第五步

到达有序区的第一位,比较完成。将暂存的元素 3 赋值到数组的首位。如下图所示。

结论

显然,这样的优化减少了许多无谓的交换过程。

动图展示

算法性能

关键字比较次数记为C,记录移动次数记为M。

时间复杂度

1、当初始序列为正序时,只需要外循环 n-1 次,每次进行一次比较,无需移动元素。此时比较次数 C_{min} 和移动次数 M_{min} 达到最小。

C_{min} = n - 1M_{min} = 0,此时时间复杂度为O(n)

2、当初始序列为反序时,需要外循环 n-1 次,每次排序中待插入的元素都要和 [0, i-1] 中的 i 个元素进行比较且要将这 i 个元素后移 i 次,加上 tmp = arr[i] 与 arr[j] = temp 的两次移动,每趟移动次数为 i+2,此时比较次数和移动次数达到最大值。

C_{max}=1+2+\cdots +(n-1)=n\ast (n-1)/2=O(n^{2})

M_{max}=(1+2)+(2+2)+\cdots +(n-1+2)=(n-1)\ast (n+4)/2=O(n^{2})

此时时间复杂度为O(n^{2})

空间复杂度

在直接插入排序中,需要了三个辅助变量,与数据规模无关,空间复杂度为O(1)

稳定性

稳定排序算法。因为相同元素的相对位置不变,如果两个元素相同,插入元素放在相同元素后面。

代码实现

C和C++

void insertionSort(int array[], int len) {
    if (len<2) {
        return 0;
    }
    
    for (int i=1; i<len; i++) {
        int insertValue = array[i];//暂存需要插入元素
        int j = i-1;

        //从右向左比较元素
        for (; j>=0 && insertValue<array[j]; j--) {
            array[j+1] = array[j];
        }

        array[j+1] = insertValue;
    }
}

Java

public static void insertionSort(int[] array) {
    if (null==array || 1==array.length) {
        return;
    }

    for (int i=1; i<array.length; i++) {
        int insertValue = array[i];//暂存需要插入元素
        int j = i-1;

        //从右向左比较元素
        for (; j>=0 && insertValue<array[j]; j--) {
            array[j+1] = array[j];
        }

        array[j+1] = insertValue;
    }
}

Python

def InsertionSort(arr):
    n = len(arr)
    for i in range(1,n):        
        temp = arr[i]
        # j保存元素temp应该插入的位置
        for j in range(i,-1,-1):
            if arr[j-1]>temp and j>0:
                arr[j] = arr[j-1]  # 后移即可
            else:
                break
        arr[j] = temp

进一步优化

插入排序标准算法在有序区查找合适位置时,采用从左到右逐一查找的方法。

我们可以采用二分查找的方法来进一步优化。

  • 93
    点赞
  • 241
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的老周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值