当你拿到一段代码时,你该如何判断这一段代码算法的好坏程度?
有的人会说跑一下(运行一下),事后统计运行时间。当然这样确实能够直观的通过看运行程序所花费时间,但是这存在着一些问题:
所以综上,如果使用事后统计(先运行一下)的方法,算法运行的速度除了和自身算法的优劣有关,还和许多的外界因素有关。并且从上我们可以提出两个问题:
至此我们引入了一个概念:算法复杂度
事前预估算法时间开销T(n)与问题规模n的关系(T代表时间“Time”)
我们使用一段简单的代码来解释时间复杂度
#include
void loveU(int n){
// n为问题规模
int i = 1;
while(i<=n){
printf("I love you %d\n times",i);
i++;
}
printf("I love you more than %d\n",n);
}
int main(){
loveU(3000);
}
在主函数中,我们调用loveU()
函数,并且传入问题规模n(3000)
我们看loveU()
函数中的5条语句频度
int i = 1 // 执行1次
while(i<=n){ // 执行3001次
printf("I love you %d\n",n); // 执行3000次
i++; // 执行3000次
}
printf("I love you more than %d\n",n); // 执行1次
故: T ( 3000 ) = 1 + 3001 + 3000 ∗ 2 + 1 T(3000) = 1+ 3001 + 3000*2 + 1 T(3000)=1+3001+3000∗2+1,得出时间开销与问题规模n的关系: T ( n ) = 3 n + 3 T(n) = 3n +3 T(n)=3n+3
推导过程:
1 + 3001 + 3000*2 + 1
-> (3001->3000+1) 1 + 1 + 1 + 3000*3
上述的例子中,问题规模n与时间开销的关系还算比较简单。
但是如果有一个表达式是: T ( n ) = n 3 + 6 n 2 + n + 1500 T(n)=n^3+6n^2+n+1500 T(n)=n3+6n2+n+1500 那么我们就很难一眼看出其复杂度。那么我们就会提出一个问题,是否可以忽略表达式某些部分呢?
答案是肯定的,我们可以忽略表达式的某些部分。
要回答这个问题,我们可以通过上述的例子来理解:
当 n = 3000 n=3000 n=3000时:
3 n = 9000 3n=9000 3n=9000 vs 3 n + 3 = 9003 3n+3=9003 3n+3=9003
n 2 = 9000000 n^2=9000000 n2=9000000 vs n 2 + 3 n + 1000 = 9010 , 000 n^2+3n+1000=9010,000 n2+3n+1000=9010,000
n 3 = 27000000000 n^3=27000000000 n3=27000000000 vs n 3 + n 2 + 9999999 = 270189999999 n^3+n^2+9999999=270189999999