插入排序与循环不变式(算法导论)

首先介绍插入排序

插入排序的名字很形象,这个算法就是从数组的第二位开始(此时第一位只有一位,视为排好序的子数组)一直到数组的最后一位,一位一位地将数加入到已经排好序的子数组中。
插入排序的初始状态,可以想象你手里有一张牌,这就是数组 0 位置的牌,从位置1到数组结束的所有数都是在桌子上放着的牌,你的目的就是把这些牌都拿到手里并排好序。
伪代码:

	for j=2 to a.length
		key=a[j]
		i=j-1
		while i>0 and a[i]>key
			a[i+1]=a[i]
			i=i-1;
		a[i+1]=key

每次阅读这些伪代码,或者刷题的时候见到大佬的代码,我总会有一种感叹:为什么他们能写出这么优雅简洁又面面俱到的代码呢?他们会在代码设计中考虑到每一种情况,比如这段伪代码中 a[i+1]=key这种设计,通过阅读《算法导论》一书,我初步窥探到一点门道,比如从插入排序的设计中的循环不变式。

循环不变式是算法中的一种概念

(以下内容参照伪代码来阅读,如果是新手下面有c语言的代码,c语言代码有注释)
比如在插入排序中,我们的子数组——排好序的手牌,就是一个循环不变式。
循环体每次循环将向数组中添加一个元素,那么子数组的长度将在1到n之间变化,可以写作A[0,1,…,j]
(其中j的值为1到n)
我们利用循环不变式的性质去验证算法的合理性。在这个例子中,子数组是有序的则循环不变式为真
循环不变式的三条性质:

  • 初始化:在这个循环第一次迭代之前,它为真(也就是当循环尚未开始的时候,我们设定的某一条件为真,比如归并排序的例子里,子数组的是排好序的这件事为真)

  • 保持:如果循环的某次迭代之前它为真,那么下一次迭代之前,它仍然为真(就是说每次循环结束前后它都为真,这一步类似数学归纳法)

  • 终止:在循环终止的时候,不变式为玩模提供一个有用的性质,该性质有助于证明算法是正确的。

    初始化对应数学归纳法中的基本情况,(思考某个等差数列的公式,根据要求,他可能要符合n=0以及n=1的情况),’‘保持’'这一步呢,s对应数学归纳法的归纳步(仍旧以等差数列对比,从第1项到第n项都要符合公式)

    终止这一步,与归纳法就有所不同了,数学归纳法是无限使用的,而这里要求在终止时结束“归纳”,并用结束归纳时带来的结论来证明算法的正确性。

初始的状态,外层循环尚未开始的状态,也就是j被赋值为1,但是还没判断 'j<a.length’的状态。此时子数组A中只有原数组a[0]中的这一个元素,那么子数组是排好序的,循环不变式为真
保持的状态,这一步是算法设计的关键,我们要在每一次外层循环的开始时,和每一次外层循环的结束时,也保证循环不变式为真,每一次循环,j都自增1,那么举例就是从j被赋值为1时,循环不变式成立;在j被赋值为2之前,循环不变式成立;有了这样的判断,我们自己也就能想出a[i+1]=key这种用来满足上述要求的操作了。
终止,我们来研究外层循环终止的时候发生了什么,并要保证此时循环不变式仍为真。终止条件是当j==时结束循环;那么此时j就走到了数组的尽头,在结束之前的最后一次循环里,j=n-1,也就是数组的最后一个元素也被插入到子数组了,此时子数组就是整个数组,而经过“保持”这一步的验证,子数组是排好序的,因此我们推断整个数组已经排好序,算法正确

C语言的代码实现
#include<stdio.h>

int main()
{
	int a[10];
	//for (int i = 0; i < 10; i++) 
	//{
	//	scanf_s("%d", &a[i]);
	//}
	int i;
	for (int j = 1; j <10 ; j++) 
	{							//我们的排序从数组的第二位开始,也就是从a[1]开始判断,
								//初始状态既是在j=1之后,
								//在判断j<10之前的子数组状态,
								//我们将排好序的数组视为子数组,此时子数组里面只有一个a[0]
		int key = a[j];			//每次将位置为j的数保存下来,
								//我们的目的是将j之前的数全部排好序
		i = j - 1;				//所以从j-1这个位置开始,依次对前面的数与j这个位置的数作比较
		while (a[i] > key && i >= 0) //只要j前面有比它大的数,就把它向后挪动,
		{
			a[i+1] = a[i];
			i--;
		}
		a[i + 1] = key;
  }


	//for (int i = 0; i < 10; i++) 
	//{
	//	printf("%d ", a[i]);
	//}

}

如有任何问题,请私信我,私信超过一天不回请加我的微信,与手机号是同一个

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值