算法和算法评价
算法的基本概念
- 定义:给定计算问题,算法是一系列良定义(定义明确无歧义)的计算步骤(计算机可实现的指令)
- 算法的特性
- 有穷性 算法必须在有限步数内输出结果
- 确定性 算法必须没有歧义
- 可行性 可以机械的一步步的执行的基本操作步骤
- 算法的优劣性比较
- 正确性 能够正确的求解出问题
- 可读性 能够帮助人理解
- 健壮性 能够对不良输入作出处理或相应的反映,而不会莫名其妙输出结果
- 高效性与低存储量性
- 算法的特性
我们在讨论时间复杂度时,首先要搞清楚的是一个程序的运行速度和什么有关。32位的计算机和64位的计算机相比,c语言和汇报语言相比,VS软件的DEV-CPP相比,都是有差距的。程序的执行速度和机械性能,编程语言和编译程序产生的机械指令都有关系。但我们现在关注的是程序算法本身,而不关注外界因素。因此后续我们讨论算法的时间复杂度时,忽略外界因素,只讨论算法程序本身在数学上的复杂程度。
算法的时间复杂度
- 时间复杂度 事先预估算法的时间开销T(n)与问题规模n的关系
我们先来观察一个程序:
//插入排序 输入:a b 两个数组,a数组为原数组,b数组为排序后的数组
int LENGTH=n;
int Insert_Sort(int a[],int b[])
{
int num=0;
for(int i=0;i<LENGTH;i++){//先将a数组的值送到b
b[i]=a[i];
}
//插入排序
for(int i=1;i<LENGTH;i++){
int key=b[i];
int j=i-1;
while((j>=0)&&(b[j]>key)){
b[j+1]=b[j];
b[j]=key;
j--;
num++;
}
}
return num;
}
上述是一个插入排序的c语言程序,我们来看一下每一句的执行频次:
//插入排序 输入:a b 两个数组,a数组为原数组,b数组为排序后的数组
int LENGTH=n;
int Insert_Sort(int a[],int b[])
{
int num=0; 执行1次
//先将a数组的值送到b
for(int i=0;i<LENGTH;i++){ 执行n+1次
b[i]=a[i]; 执行n次
}
//插入排序
for(int i=1;i<LENGTH;i++){ 执行n次
int key=b[i]; 执行n-1次
int j=i-1; 执行n-1次
while((j>=0)&&(b[j]>key)){ 执行n-1——(n-1)*(n-1)次
b[j+1]=b[j]; 执行n-2——(n-1)*(n-2)次
b[j]=key; 执行n-2——(n-1)*(n-2)次
j--; 执行n-2——(n-1)*(n-2)次
num++; 执行n-2——(n-1)*(n-2)次
}
}
return num; 执行1)次
}
将所有次数加起来为:10n-8——9n^2-9n+10
- 时间复杂度分析
- 是否可以忽略时间开销表达式中的某些部分
- 可以只考虑阶数高的部分
- 问题规模足够大时,常数项系数可以忽略
- 如果代码数较多,需要一条一条分析时间复杂度吗?
- 顺序执行的代码只会影响常数项,可以忽略
- 只需要挑选循环中的一个基本操作分析它的执行次数与n的关系即可
- 如果有多重嵌套循环,只需关注最深层循环循环了几次
- 计算时间复杂度
- 找到一个基本操作(最深层循环)
- 分析该基本操作的执行次数x与问题规模n的关系x=f(n)
- x的数量级O(x),就是算法时间复杂度T(n)
- 常用技巧
- 加法规则:
- 乘法规则:
- 三种复杂度
- 最坏时间复杂度
- 平均时间复杂度
- 最好时间复杂度
- 是否可以忽略时间开销表达式中的某些部分
算法的空间复杂度
- 计算空间复杂度
- 普通程序
- 找到所占空间大小与问题规模相关的变量
- 分析所占空间x与问题规模n的关系x=f(n)
- x的数量级O(x)就是算法空间复杂度S(n)
- 递归程序
- 到递归调用的深度x与问题规模n的关系x=f(n)
- x的数量级O(x)就是算法空间复杂度S(n)
- 注:有的算法各层函数所需存储空间不同,分析方法略有不同
- 普通程序
- 常用技巧
- 加法规则
- 乘法规则