003 插入排序python实现

"""
插入排序原理讲解,此处采用升序排序进行讲解,原序列为[5, 4, 3, 2, 1],插入排序后的序列为[1, 2, 3, 4, 5]
我们的排序仍然需要经过若干轮,每轮经过若干次。
首先,每一轮,都从逻辑上将序列一分为2,左边一部分,右边一部分。
注意:分的时候一定要有技巧,左侧部分的序列一定是已经排好序的,右侧序列中的元素是等待排序等待的。
所以,如果是第一轮,则左侧序列仅能保留一个元素,剩下的元素在右侧序列,因为如果只有一个元素那么是无需排序的。
就本案例来说,从逻辑上看,第一轮左侧部分序列为[5],右侧部分序列为[4,3,2,1]
特别注意:这只是逻辑上的划分,他们仍然是在同一个序列上。

然后,每一轮,按照从左到右的顺序都只从右侧序列中拿一个元素,并将其存入变量key中,
就本案例来说,第一轮右侧序列的第一个元素为4,key=4
每一轮从右侧拿出一个元素之后,其余的元素就都不用看了。
即每一轮,仅需要将右侧序列中的一个元素与左侧有序的序列中的所有元素一起排好顺序即可。

分析:
每一轮中,右侧序列的第一个元素的下标,比左侧序列的最后一个元素的下标大1。
每一轮中,右侧序列的第一个元素都会提前存入到key中。
每一轮的若干次中,都是在拿key与左侧序列的元素进行排序比较(为什么呢?看到后面就会明白)

排序开始:
第一轮:拿出右侧序列中的第一个元素,就本案例来说为4,提前存到一个变量中,即key = array[1],它为需要排序的元素
然后与左侧序列中的最后一个元素,进行比较,此时左侧序列中,仅有一个元素,就本案例来说为5,他的下标为0,
注意:
时刻记住左侧序列是已经排好序的,由于我们此处拿升序作为讲解,所以左侧序列的最后一个元素为整个左侧序列中最大的元素。
为什么一定要先和左侧序列的最后一个元素进行比较而不是先和左侧序列的第一个元素进行比较呢?因为方便,我们可以观察一下,
左侧序列的最后一个元素的下标正好比右侧序列的第一个元素的下标小1,所以编程起来会更加方便。

比较时情况如下
左侧序列最后一个元素 小于 key
左侧序列最后一个元素 等于 key
左侧序列最后一个元素 大于 key


比较运算开始
1、如果左侧序列最后一个元素 小于 右侧序列的第一个元素,那么右侧序列的第一个元素大于左侧的所有元素(因为左侧序列是有序的,
最后一个元素就是左侧序列中最大的元素)那么保持原位置即可,此时从左侧的第一个元素到右侧的第一个元素都是有序的,本轮结束
2、如果左侧序列最后一个元素 等于 右侧序列的第一个元素,那么右侧序列的第一个元素大于等于左侧的所有元素(因为左侧序列是有序的,
最后一个元素就是左侧序列中最大的元素)那么保持原位置即可,此时从左侧的第一个元素到右侧的第一个元素都是有序的,本轮结束
3、如果左侧序列最后一个元素 大于 右侧序列的第一个元素,那么此时需要继续拿key与左侧序列倒数第二个元素进行比较,最多比到左侧的第一个元素,
目的是为了找到首个小于或者等于key的元素,如果找到了,则就无需在找了,就可以进行相应的操作了,具体流程如下:
    例如array[i] <= key,此时,需要将key插入到位置i的后面,array[i+1] = key,此时,原array[i+1]就被覆盖了,所以在插入之前,
    原i+1处的元素及其往后的每一个元素都要提前往后移动一位。即在插入前,原i+1位置的元素已经移动到了i+2位置上,原i+2位置上的元素移动到了
    i+3的位置上,以此类推,左侧最后一个元素正好移动到了右侧第一个元素的位置处,原右侧第一个元素被覆盖了。但是右侧第一个元素已经提前存入到key中,
    所以不怕被覆盖,也就是为什么要拿key去与左侧序列中的元素进行比较,因为右侧序列第一个位置是可能被占用的。而且插入的前提就是要先把即将插入
    的位置空出来,就是需要后面的元素逐个后移。

    所以,当左侧元素 大于 key时,操作如下:
    1、左侧元素向后移动一位,即 array[i + 1] = array[i], 注意:此时array[i+1]和array[i]存的是同一个元素。
    2、继续将左侧前一个元素与key进行比较:
        大于 key:
            左侧当前元素继续向后移动,
            左侧前一个元素继续与key比较
        小于或者等于:此时,我们就找到了该元素,例如就是array[i],此时,查找结束。
        最后key存入当前元素的后面的位置,即array[i+1] = key

就本案例来说,我们来模拟下插入排序的流程(升序)
第一轮:[5], [4, 3, 2, 1], key = 4
第一次:5 > 4, 将元素5往后移动一位,此时序列情况为[5, 5, 3, 2, 1],
key再去与元素5前面的元素进行比较,由于没有,则直接将key插入到元素5的原位置处,结果为:[4, 5, 3, 2, 1]

第二轮:[4, 5], [3, 2, 1], key = 3
第一次:5 > 3, 将元素5往后移动一位,此时序列情况为[4, 5, 5, 2, 1],key再去与元素5前面的元素进行比较,
第二次:4 > 3, 将元素4往后移动一位,此时序列情况为[4, 4, 5, 2, 1],key再去与元素4前面的元素进行比较,
由于没有,则直接将key插入到元素4的原位置处,结果为:[3, 4, 5, 2, 1]

第三轮:[3, 4, 5], [2, 1], key = 2
第一次:5 > 2, 将元素5往后移动一位,此时序列情况为[3, 4, 5, 5, 1],key再去与元素5前面的元素进行比较,
第二次:4 > 2, 将元素4往后移动一位,此时序列情况为[3, 4, 4, 5, 1],key再去与元素4前面的元素进行比较,
第三次:3 > 2, 将元素3往后移动一位,此时序列情况为[3, 3, 4, 5, 1],key再去与元素3前面的元素进行比较,
由于没有,则直接将key插入到元素3的原位置处,结果为:[2, 3, 4, 5, 1]

第四轮:
第一次:5 > 1, 将元素5往后移动一位,此时序列情况为[2, 3, 4, 5, 5],key再去与元素5前面的元素进行比较,
第二次:4 > 1, 将元素4往后移动一位,此时序列情况为[2, 3, 4, 4, 5],key再去与元素4前面的元素进行比较,
第三次:3 > 1, 将元素3往后移动一位,此时序列情况为[2, 3, 3, 4, 5],key再去与元素3前面的元素进行比较,
第四次:2 > 1, 将元素2往后移动一位,此时序列情况为[2, 2, 3, 4, 5],key再去与元素2前面的元素进行比较,
由于没有,则直接将key插入到元素3的原位置处,结果为:[1, 2, 3, 4, 5]

具体代码如下:
def sort_insertion(array):
    length = len(array);
    for i in range(1, length): # 右侧待排序的数组,下标从1开始,一直到length-1
        key = array[i]; # 右侧待排序的元素,提前存起来,
        j = i - 1; # 找到左侧最后一个元素的下标

        while  j >= 0 and array[j] > key: # 遍历到左侧第一个元素,array[j] 小于或者等于 key,则什么都不需要做,此时可以直接结束比较。
                array[j+1] = array[j]; # 将大于key的元素逐个往后移动一位。
                j -= 1;

        array[j+1] = key; # 此处什么要j + 1呢?看如下解释
            #当while j >= 0 全部执行完毕时(没有走break语句)此时j = -1,此时,左侧的每个元素都往后挪了一位,所以此时j + 1 = 0 正好放k
            #当break终止时,array[j] <= key,则可以就应该在array[j+1]的位置上。此处不用担心覆盖的问题,因为当前的j+1为值的元素已经
            #在j+2处存好了。
    return array;

通过观察我们发现,插入最多需要进行n(n-1)/2
"""

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值