第二章-算法基础-2.1-插入排序

本文详细介绍了插入排序的工作原理,通过扑克牌和试卷排序的例子来帮助理解。插入排序的伪代码展示了一个从无序序列中逐步构建有序序列的过程,并通过循环不变式的三个性质(初始化、保持、终止)来证明算法的正确性。此外,还提供了逆序插入排序的伪代码以及查找特定值的算法及其正确性证明。
摘要由CSDN通过智能技术生成

  2.1 插入排序(Insertion sort)

提示:这一部分由问题、如何理解插入排序、插入排序中的伪代码及原理、循环不变式与插入排序的正确性组成,其中循环不变式与插入排序的正确性是个人认为比较晦涩的部分,但是我认为它里面蕴含的思想是非常值得我们去参考的。

1、问题:

        输入:一个数的序列<a1,a2,a3,a4>

        输出:<a1',a2',a3',a4'>,其中满足递增顺序

2、如何理解插入排序:

 以扑克牌为例,我们每从桌上抽取一张扑克牌时,都会将扑克牌插入到左手正确的位置(让其从左到右递增排列)。为了找这个正确的位置,我们会从右到左将它和已经在左手中的每张牌进行比较,如果刚好小于某一张牌,那么将我们抽取的牌放置在这张牌的左边,如图,左手中的牌就处于正确的顺序中。

如果没打过牌,那么我们也可以用试卷排序来理解,我们老师在排试卷的时候是从高到低排,假设为左高右低,当我们抽出一张未排序的试卷,将其放入已经排序好的那些试卷中,我们往往需要和右边的依次卷子比较,找到一个正确的位置,使这张试卷的分数正好在左右两边试卷分数的区间内。

3、插入排序的伪代码及原理

for j = 2 to A.length  //从第二个元素开始循环
    key = A[j]         
    //Insert A[j] into sorted sequece A[1...j-1](将A[j]插入到之前已经排序好的A[1...j-1]序列中)
    i = j - 1                   //i就相当于已经排序好的最后一张牌,就是待排序牌的前一张牌
    while i > 0 and A[i] > key  //依次比较,当前面的牌大于待排序牌的时候
        A[i+1] = A[i]           //我们将前面的牌向后移动一位
        i = i - 1               //然后角标-1,即将比较对象往前再推一位
    A[i+1] = key               //while循环结束后,待排序牌已经找到了正确的位置,进行赋值操作即可

以排序<5,2,4,6,1,3>为例:

【1】此图标记对应伪代码:

        (1)黑色长方形:A[j]

        (2)灰色长方形:A[1...j-1]

        (3)灰色箭头:A[i+1] = A[i] 和 i = i - 1 ,即将符合while条件的数字均向后移动一位

        (4)黑色箭头:A[i+1] = key ,待排序元素被移动到的地方

【2】具体解释:如图所示,我只解释d->e的变化过程,其他过程也和这个原理相同

        (1)可以发现1作为A[j]比前面任何一个数字都要小。

        (2)此时将前面的比1大的数字依次往后挪一位。

        (3)最后将1赋值到第一位上。

4、循环不变式与插入排序的正确性

【1】循环不变式(loop invariant)是在循环体的每次执行前后均为真的谓词。

【2】循环不变式拥有以下性质,证明算法的正确性时需要用到这些性质:

 (1)初始化(Initialization):循环第一次迭代之前,它为真。(相当于数学归纳法中k=1)

 (2)保持 (Maintenance):如果循环的某次迭代之前它为真,那下次迭代之前它也为真。(相当于数学归纳法中假设k=n时成立,证明k=n+1时也成立)

 (3)终止(Termination):循环终止时,不变式为我们提供一个有助于证明算法是正确的性质。(在数学归纳法中,k是可以无穷大的,但是终止说明了这里和数学归纳法的不同之处在于这里的k是有限度的,即到不符合循环的条件为止)

【3】在插入排序中,证明这些性质的正确性:

 (1)初始化:当j = 2时,在子数组A[1...j-1]只有一个元素A[1]时,循环不变式显然成立(子数组按序排列)。

 (2)保持:先将A[1..j-1]中大于A[j]的元素均向右移动一位,找到A[j]的正确位置并将其插入,此时产生的子数组A[1...j]由原来数组中第1到第j个元素产生,且符合递增条件,因此其迭代保持循环不变式。

 (3)终止:在循环终止时,j > A.length即 j > n,因此最后必有 j = n + 1,此时子数组A[1...j-1]

(即A[1...n])由未排序前的原数组A[1...n]组成,且这一子数组已经经过排序,我们知道A[1...n]就是整个数组,因此可以知道整个数组已经排序,因此算法是正确的。

5、Exercise

2。1-1:按照上面的图片就可以做出

2.1-2:将A[i] > key中的>改为<即可

for j = 2 to A.length  //从第二个元素开始循环
    key = A[j]         
    //Insert A[j] into sorted sequece A[1...j-1](将A[j]插入到之前已经排序好的A[1...j-1]序列中)
    i = j - 1                   //i就相当于已经排序好的最后一张牌,就是待排序牌的前一张牌
    while i > 0 and A[i] < key  //依次比较,当前面的牌小于待排序牌的时候
        A[i+1] = A[i]           //我们将前面的牌向后移动一位
        i = i - 1               //然后角标-1,即将比较对象往前再推一位
    A[i+1] = key               //while循环结束后,待排序牌已经找到了正确的位置,进行赋值操作即可

2.1-3:

for i = 1 to A.length
    if A[i] == v
        return i   //查找到返回i
return NIL

证明算法正确性:

(1)初始化:只有一个元素的时候,如果A[i] = v,那么会return i,如果不等,那么return NIL,符合条件

(2)保持:同理,如果检索到A[i] = v,那么return i,否则一直检索下去,直到符合条件或者没有任何数可以与v相等,因此其迭代保持循环不变式

(3)终止:假如中途A[i]=v,程序终止,说明找到了v,假如未能找到,那么最后当i=A.leng+1时,返回NIL,因此符合循环不变式

2.1-4

思路:从右到左逐个相加,同时如果是两个1相加,左边数字加1

for i = n downto 2
    C[i] = A[i] + b[i]
    if(c[i] > 1)
        c[i] = 0
        c[i-1] += 1
   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

KeepCoding♪Toby♪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值