插入排序
排序问题
输入:长度为n的一个数字序列。(希望排序的数也称为关键词)
输出:包含输入序列所有元素的有序序列。
伪代码如下:
INSERTION-SORT(A)
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
伪代码规则要点
1.缩进块表示代码结构。
2.对于for循环用关键词to表示每次迭代增加循环计数器,用downto表示每次迭代减少循环计数器,用by表示计数器的步长。
for i = 2 to 10 by 2 //从2开始以2为步长增加至10
for i = 2 downto 10 by 2 //从2开始以2为步长减少至10
3.局部定义的变量没有特别说明不当做全局变量使用。
4.符合数据组织为对象,对象包含属性。属性可以串联。
5.调用参数按值传递,把参数的副本传递给过程。当对象作为参数传递时,对象的指针被复制传递。
6.一个return语句立即将控制返回到调用过程的调用点,不同于普通代码的是允许一个return返回多个值。
7.布尔运算(and or)是短路的,会先求取左边的值得正确性,有必要再求取右边的正确性。
8.关键词error表示已被调用的过程情况不对而出现的一个错误,伪代码中不需要写出采取什么行动。
分析算法
1.分析算法的方法是对每一步调用的次数进行计数,然后忽略掉每一步用的时间常数(这和计算机的性能相关)。最后对步数求和,取最高次数项表示算法复杂度。
例如上图是对插入排序的分步开销统计,单次循环求和公式如下:
忽略掉常数之后实际上T(n)=n,之后对算法总开销求和相当于是一个整数列求和,也就是一个算术级数。
上式结果最高次项为n的平方,所以插入排序算法复杂度为(n^2)。
最坏情况和平均情况分析
对于算法的复杂度我们一般关注的是算法运行的最坏情况和平均情况。最坏情况表示算法的最低效率,而平均情况则表示大多数时候的运行效率。
实际上主要关注最坏情况,原因有以下几点:
1.算法的最坏情况表示了一个算法运行时间的上界,是一个可以确定的承诺。
2.对于某些算法最坏情况经常出现。例如检索数据库的算法,当信息不在数据库出现的时候算法则会以最坏的情况运行。
3.对于大多数算法平均情况往往和最差情况一样差。
增长量级
通常我们只对一个算法的增长量级感兴趣,即时间开销的最高次项。但对于增长量级高的算法并不是没有存在的必要,在一定范围内次数高的算法也可能会取得较好的效果,例如高增长量级的算法输入范围小于低增长量级算法时。
设计算法
分治法
许多有用的算法在结构上是递归的,在思想上通常遵循分治法。即首先对一个问题分解为几个规模较小的问题,然后对小的问题进行递归求解,再合并这些子问题的解。
分治模式在每层递归均具有下列三个步骤:
分解原问题为若干子问题,这些子问题是原问题的规模较小的实例。
解决这些子问题,递归的求解个子问题。如果子问题规模足够小就直接求解。
合并这些子问题的解形成原问题的解。
递归排序
递归排序重要的点在于归并过程。由于散乱的序列分成数个子序列后各个序列间的值可能相互穿插,因此不能直接将子序列拼接起来,而是需要依次判断每一个位置的数的大小。
递归排序伪代码如下:
MERGE-SORT(A)
n1 = q-p+1
n2 = r-q
let L[1..n1+1] and R[1..n2+1] be new arrays
for i = 1 to n1
L[i] = A[p + i - 1]
for j = 1 to n2
R[j] = A[q+j]
L[n1+1] = oo //给一个停止标志位
R[n2+1] = oo
i = 1
j = 1
for k = p to r
if L[i] <= R[j]
A[k] = L[i]
i = i+1
else
A[k] = R[j]
j = j+1
c++代码如下:
#include <stdio.h>
#include <iostream>
using namespace std;
int MERGE_SORT(int A[],int L);
int main()
{
int A[] = {32,3254,37,42,3,12,431,231,24,543,6,34,5,4235,346,457,645};
int L = sizeof(A) / sizeof(A[0]);
MERGE_SORT(A,L);
for(int y=0;y<L;y++)
{
cout<<A[y]<<endl;
}
getchar();
return 0;
}
int MERGE_SORT(int A[],int L)
{
if(L==1)
{
return *A;
}
int l1=L/2,l2=L/2;
if(L%2==0)
{
l2=l2;
}
else
{
l2=l2+1;
}
int B[l1]={0};
int C[l2]={0};
int i=0;
int k=0;
for( i=0;i<(L/2);i++)
{
B[i] = A[i];
}
for( k=(L/2);k<L;k++)
{
C[k-L/2] = A[k];
}
MERGE_SORT(B,l1);
MERGE_SORT(C,l2);
int h=0,g=0;
for(int n=0;n<L;n++)
{
if(h<l1||g<l2)
{
if(h==l1)
{
A[n] = C[g];
g = g + 1;
continue;
}
if(g==l2)
{
A[n] = B[h];
h = h + 1;
continue;
}
if(B[h]<C[g])
{
A[n] = B[h];
h = h + 1;
}
else
{
A[n] = C[g];
g = g + 1;
}
}
}
return *A;
}
递归排序的算法复杂度示意图:
其中的lgN表示的是log2N。