《大话数据结构》中对算法的时间复杂度定义如下:
“算法分析时,语句的总执行次数T[n]是关于问题规模n的函数,进而分析T[n]随n的变化情况并确定T[n]的数量级。算法的时间复杂度,也就是算法的时间度量,记做T[n]=O(f[n]),表示随问题规模n的增大,算法的执行时间的增长率和f[n]的增长率相同,称做算法的渐进时间复杂度,简称时间复杂度,f[n]是问题规模n的某个函数。”
根据定义举个例子,计算1+2+3+4+···+100 = ?
int sum = 0 ,n=100;
for(int i=1;i<=n;i++){
sum+=i;
}
算法执行完毕时,语句的总执行次数T[n] = 1+n+1+n = 2n+2,随着问题规模n的增大,算法的执行时间的增长率和f[n]的增长率相同,其中f[n] = 2n,因为随着n的增大,常数2并不增大,为了简化计算,取主要的执行次数2n,这样的话T[n] = O(2n)。 也就是O(n) 【下文解释为什么去掉2】
同一个问题,我们使用高斯的算法解决。
int sum = 0,n = 100;
sum = (n*(n+1))/2;
算法执行完毕时,语句总执行次数为2,T[n]=O(2)。也就是O(1)【下文解释】
通过以上例子,给出“大O阶”的推倒(ノ*・ω・)ノ 步骤:
1.计算语句总执行次数得出f[n]
2.用常数1取代运行此书中的所有加法常数
3.在修改后的运行次数函数中,只保留最高阶项
4.如果最高阶项存在且不是1,去除于这个项相乘的常数
再举个例子
int n=100;
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
n--;
}
}
printf("n=%d",n);
第一句很明显执行1次,嵌套循环的执行次数一眼看不出来,这就需要分析了,先看内循环,j自增一次,n自减一次,可以推测当n自减到n/2时,第一次内循环结束,此时第二句执行了一次。接下来看第二次内循环,当n减到n/4的时候,第二次内循环结束,第二句执行了第二次。由此可知,当i>=n/(2i)时,外循环也就是第二句结束,也就是说外循环最多运行n/(2i)次,当n等于一百万的时候,i等于16,所以外循环可以忽略不计,内循环 n--; 执行了n-n/(2i)次,约等于n次。所以这个例子的算法时间复杂度为O(n)。
上面那个例子是我自己编的。最开始计算外层循环的时候也迷糊了,在群里和大家讨论了才明白,这也告诉我们,计算复杂度的时候,一定要有全局观,不要死磕每一句的执行次数。今早在看《编程珠玑》时,了解了近似估算,发现也可以用在这个地方。
最后给出各种复杂度的图像,绘图工具使用的是Mathematica,感觉是比Matlab更好用的数学工具。
删除几个增长率快的