二、插入排序
排序算法如下:
//例 11.1 插入排序
#include<stdio.h>
#define LEN 5
int a[LEN]={10,5,2,4,7};
void insertion_sort(void){
int i,j,key;
for(j=1;j<len;j++){
key=a[j];
i=j-1;
while(i>=0&&a[i]>key){
a[i+1]=a[i];
i--;
}
a[i+1]=key;
}
}
int main(void){
insertion_sort();
return 0;
}
如何严格证明这个算法是正确的?换句话说,只要反复执行该算法的for循环体,执行LEN-1次,就一定能把数组a排好序,而不管数组a的原始数据是什么,如何证明这一点?我们可以借助Loop Invariant的概念和数学归纳法来理解循环结构的算法,假如某个判断条件满足以下三条准则,它就称为loop invariant:
- 第一次执行循环体之前该判断条件为真
- 如果”第N-1次循环之后(或者说第N次循环之前)该判断条件为真“这个前提可以成立,那么就有办法证明第N次循环之后该判断条件为真
- 如果在所有循环结束后该判断条件为真,那么就有办法证明该算法正确地解决了问题
只要我们找到这个Loop Invariant,就可以证明一个循环结构的算法是正确的。上述插入排序算法的Loop Invariant是这样的判断条件:第j次循环之前,子序列a[0,…,j-1]是排好序的。接下来我们验证下Loop Invariant的三条准则:
- 第一次执行循环之前,j=1,子序列a[0,…,j-1]只有一个元素a[0],只有一个元素的序列显然是排好序的
- 第j次循环之前,如果”子序列a[0,…,j-1]是排好序的“这个前提成立,按照该算法的步骤,把a[j-1]、a[j-2]、a[j-3]等等比key大的元素都依次往后移一个,直到找到合适的位置给key插入,就能证明循环结束时子序列a[0,…,j]是排好序的。
- 当循环结束时,j=LEN,如果”子序列a[0,…,j-1]是排好序的“这个前提成立,那就是说a[0,…,LEN-1]是排好序的,也就是说整个数组a的LEN个元素都是排好序的
可见,有了这三条,就可以用数学归纳法证明这个循环是正确的。