学完第一篇的概念,不知道大家对数据结构有没有感觉呢?接下来我们介绍一下和算法有关的概念。说道算法,可能大多数同学会感觉很难,的确算法是一块硬骨头,但是学好它有重要的意义。图灵奖得主,计算机科学家N.Wirth(沃斯)提出:
程序 = 算法 + 数据结构
。所以,作为程序开发人员不仅要懂数据结构,还要和算法结合才能写出优秀的程序。
算法定义:
- 算法是解决特定问题具体步骤的描述,对于特定某个问题,给定一个输入后,计算机按照算法可以给出对应的输出。
特征
有穷性 (Finiteness)
- 算法的有穷性是指算法必须能在执行有限个步骤之后终止;
确切性 (Definiteness)
- 算法的每一步骤必须有确切的定义;
输入项 (Input)
- 一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法本身定出了初始条件;
输出项(Output)
- 一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;
可行性 (Effectiveness)
- 算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步,即每个计算步都可以在有限时间内完成(也称之为有效性)。
算法设计的基本原则
正确性
- 算法的正确性是评价一个算法优劣的最重要的标准。
可读性
- 算法的可读性是指一个算法可供人们阅读的容易程度。
健壮性
- 健壮性是指一个算法对不合理数据输入的反应能力和处理能力,也称为容错性。
时间效率高和低存储量 (最重要)
- 对于一个问题的解决,应该力求高效地解决,也就是花费时间越短越好,同时低存储量这里指的是对内存的消耗应该尽量降低。
算法的时间复杂度分析 —–用渐进分析的角度
- 我们用T(n)来表示语句的总的执行次数。对于每一个n,都有一个T(n)与之对于,但是怎么样才能有一个评判标注来描述算法的复杂程度呢?是用T(n)的AVERAGE 情况来判断吗?还是用T(n)的最好情况,还是最坏情况来判断呢?为了有一种BIG IDEA(我理解为大局观,出自麻省理工学院算法导论课程)的角度去分析这个算法的复杂度,我们往往以一种悲观的角度来考虑它的时间复杂度(即下图中T(n)函数的upper bound来描述)。此时,我们引入一个notation – BIG - O,即T(n) = O(f(n))来表示对算法时间复杂度的分析,T(n) = O(f(n)) <=> 存在常数n > 0, n0 > 0,使得 T(n) <= c * f(n),我们可以把大O符号看作是 <= 符号。
BIG-O阶数
常量阶
- 一般常量阶为O(1),无论具体有几句,只要是一个Constant(常数),我们就用O(1)来表示。应该注意的是,只要程序执行次数不受n大小而改变,我们都应该判断出这是O(1)
线性阶
- O(n)为线性阶
#include <iostream>
using namespace std;
int main()
{
int n;
int i;
for (i = 0;i <= n;i ++)
{
//执行一条为O(1)的语句;
}
}
对数阶
- 下面的代码,时间复杂度又是多少呢?
int count = 1;
while (count < n)
{
count *= 2;
/*时间复杂度为O(1)语句*/
}
由于count每次乘上2后就更接近n,所以有多少个2相乘后≥n,就循环多少次,2^x = n, x = log2(n)。所以算法的时间复杂度为O(logn)
平方阶
int i,j;
for (i = 0 ; i < n ; i ++)
{
for (j = 0; j < n; j ++)
{
/*时间复杂度为O(1)语句*/
}
}
外面的循环语句执行n + 1 次,循环执行n次,同理里面也执行n次,所以我们说这种情况的时间复杂度为O(n^2)
总结
看了那么多情况,我们来总结一下各种情况: