插入排序
1 算法执行
1.1 输入与输出
Funtion Name: INSERTION-SORT
Input: array of <a1, a2, ..., an>;
Output: array of <a1’, a2’, …, an’>, a1’<=a2’<=…<=an’;
1.2 伪代码:
INSERTION-SORT(A)
for j = 2 to A.length
key = A[j]
//Insert A[j] into the sorted sequence A[1…j-1].
i = j - 1
while i > 0 and A[i] > key
A[i + 1] = A[i]
i = i - 1
A[i + 1] = key
1.3 C++代码:
#include<iostream>
using namespace std;
template<typename T>
void insertionSort(T* a, int length)
{
T key;
int i;
for (int j = 1; j < length; j++)
{
key = a[j];
i = j - 1;
while (i >= 0 && a[i] < key)
{
a[i + 1] = a[i];
i--;
}
a[i + 1] = key;
}
}
template<typename T>
void Printf(T* a, int length)
{
cout << "Result after insertionSort:" << endl;
for (int i = 0; i < length; i++)
{
cout << a[i] << "\t";
}
cout << endl;
}
int main()
{
int a[6] = {
5,2,4,6,1,3 };
insertionSort<int>(a, 6);
Printf<int>(a, 6);
system("pause");
}
1.4 运行结果:
2 算法分析
2.1 过程描述
INSERTION-SORT(A)
for j = 2 to A.length
key = A[j]
//Insert A[j] into the sorted sequence A[1…j-1].
i = j - 1
while i > 0 and A[i] > key
A[i + 1] = A[i]
i = i - 1
A[i + 1] = key
类似打扑克时(默认手牌按序排列)每次都从牌堆里抓一张牌,然后向手牌里插入,当然,正常我们的插入是直接把牌插在他该插入的地方,而在计算机里是前面的小牌不动,后面的大牌都往后挪一格,给抓到的新牌空出位置。
(注意,C++和伪代码的数组前者是从0开始而后者是从1开始,以下描述基于Cpp规则)
- 第一次循环:j = 0时不必排序,第一张牌直接放手里即可。
- 第二次循环:j = 1时进入循环。key = A[1],i = j - 1 = 0,判决i >= 0 且 A[i] > key,满足,故A[1] = A[0],即用5覆盖2,然后i = i - 1 = -1,推出while循环。然后A[0] = 2。此时前两个数2,5就排好序了。
- 第三次循环:j = 2时相当于又摸了一张4,手里牌时2和5。key = A[2],i = j - 1 = 1,判决i >= 0且A[i] > key, 满足,故A[2] = A[1],即用5覆盖4。此时前三个数为255,key依旧为4。i = i - 1 = 0返回while循环,此时A[i] > key不满足,退出循环。然后A[i+1] = A[1] = key = 4,即由此确定了4该插入的位置,前三个数排好了。
- …以此类推。
每一次循环均可使排好序的前一部分序列多一个数字,以此类推,到最后可以使得整个序列完成排序。这里每一次循环都会出现比上一次循环多一个数的排序序列,称之为这一算法的循环不变式。
2.2 循环不变式
在编写循环时,找到让每次循环都成立的逻辑表达式很重要。这种逻辑表达式称为循环不变式(loop invariant)。循环不变式相当于用数学归纳法证明的“断言”。循环不变式用于证明程序的正确性。
循环不变式必须满足三个性质(即证明过程):
- 初始化(Initialization):
它在循环的第一轮迭代开始之前,应该是正确的。 - 保持(Maintenance):
如果在循环的某一次迭代开始之前它是正确的,那么,在下一次迭代开始之前,它也应该保持正确。 - 终止(Termination):
当循环结束时,不变式给了我们一个有用的性质,它有助于表明算法是正确的。
我们可以和数学归纳法对比来看。数学归纳法的证明过程如下(以自然数为例):
- 奠基:即在初始n = 0下成立
- 递推:证明在n = m下成立时,n = m + 1同样成立
对比二者的关系,对于循环不变式,我们可以发现其原理和数学归纳法类似。若要证明每一步都满足循环不变式(断言),则可以按照奠基和递推的方式来证明,不过算法肯定是有终止的,而关于自然数的证明大部分是无休止的(自然数无限多),故算法也要满足在循环结束后算法的输出也是正确(书中称为“真”,其含义为循环不变式是一个逻辑表达式,其逻辑值为true)。
在这一算法中,其循环不变式为:
按循环不变式的三条性质证明如下(按伪代码为例):
- 初始化:
在循环的第一轮迭代开始前,即j = 2,此时子数组只有一个元素A[1],自然满足排序和子数组两个约束。 - 保持:
假设循环进行到第k次后,第k + 1次开始之前,循环是正确的,也就是说,此时前k个数A[1…k]是前k个数排好序的数组,也是原A[1…k]的子数组。那么第k + 1次执行后,key = A[k + 1],i = k,以上两个初值赋完后进入while循环。首先对A[1…k]中后面的数进行判断,如果后面的数大,就向后移位,i自减,判断下一个;如果i < 0(全判断完)或A[i] <= key(数据已经找到了该插入的位置)&#x