插入排序是一种很重要的排序方式,包括直接插入排序,二分插入排序,希尔排序等,这里 以直接插入排序(稳定)为例研究算法分析。
直接插入排序最经典的引入就是扑克牌,所谓插入就是拿着当前牌(第j张),插入到前j-1张牌中,使这j张牌仍然有序,所以伪代码是这样的(n张牌, 储存在数组A[n+1]中):
//扑克牌假定从1到n,第一张显然已经有序,当然插入的牌也有可能插到当前第一张牌之前,也就是第0位
for(i = 2; i<=n;i++)
{
key = A[i];
j = i-1;
while(j>0&& A[j]>key)
{
A[j+1] = A[j];
j--;
}
A[j+1] = key;
}
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
int n;
while(cin>>n)
{
int A[n+2];
for(int i = 1; i<=n; i++)
cin>>A[i];
for(int i = 2; i<=n; i++)
{
int key = A[i];
int j = i-1;
while(j>0&& A[j]>key)
{
A[j+1] = A[j];
j--;
}
A[j+1] = key;
}
for(int i = 1; i<=n; i++)
cout<<A[i]<<" ";
cout<<endl;
}
}
循环不变式:
在上述伪代码中 for(int i = 2; i<=n; i++)的每一次循环,前i-1项都是已经排好序的,这种性质叫做循环不变式,可以用来帮助我们理解算法的正确性,关于循环不变式,必须证明三条性质:
1、初始化: 循环的第一次迭代之前为真
2、保持: 如果循环的每次迭代之前为真,那么下次迭代前仍为真
3、终止: 在循环终止时,不变式为我们提供一个有用的性质,该性质有助于证明算法是正确的
分析算法:
单处理计算模型:RAM模型(随机访问机),作为算法分析时要使用的实现技术模型,包括描述所用资源及其代价。在RAM模型中,指令一条借一条的执行,没有并发操作。
插排算法分析:
通常把一个程序的运行时间描述成其输入规模的函数,一个算法在特定输入上的运行时间是指执行的基本操作数或步数,假定第i行每次执行需要时间ci(常量),那么上述伪代码的执行时间如下
代价 次数
for(i = 2; i<=n;i++) c1 n
{
key = A[i]; c2 n-1
j = i-1; c3 n-1
while(j>0&& A[j]>key) c4 ∑_(i=2)^n▒ti
{
A[j+1] = A[j]; c5 ∑_(i=2)^n▒(ti-1)
j–; c6 ∑_(i=2)^n▒(ti-1)
}
A[j+1] = key; c7 n-1
}
最坏的情况(ti为 n)
时间为 an^2 + bn + c;但我们真正感兴趣的是运行时间的增长率||增长量级,所以只需要考虑最重要的项,记做Θ(n^2);