这样用大写O()来体现算法时间复杂度的记法,我们称之为大O记法。一般情况下,随着n的增大,T(n)增长最慢的算法为最优算法。
举例,如果一个算法对于任何大小为n的输入,它至多需要5n3+3n的时间运行完毕,那么它的渐近时间复杂度是O(n3) 。
注意:
时间复杂度计算过程中,是最终会去掉这个函数的低阶项和首项系数的。(这也就是为什么5n3+3n 会最终化简为n3)
举例:
一:O(1) 常数阶
Temp=i;i=j;j=temp;
以上三条单个语句的频度均为1,该程序段的执行时间是一个与问题规模n无关的常数。算法的时间复杂度为常数阶,记作T(n)=O(1)。如果算法的执行时间不随着问题规模n的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数。此类算法的时间复杂度是O(1)。
二: O(n) 线性阶
通常,如果某个循环结构以线性方式运行n次,并且循环体的时间复杂度都是O(1),那么该循环的复杂度就是O(n).
for(int i = 0; i < n; i++){ //一些列复杂度为O(1)的步骤.... }
即使,该循环跳过某些常数部分,只要跳过的部分是线性的,那么该循环体的时间复杂度仍就是O(n).
比如
int count = 1; while(count < n){ count += 2; //一些列复杂度为O(1)的步骤.... }
时间复杂度还是O(n)
三:O( log2n) 对数级
int count = 1; while(count < n){ count *= 2; //一些列复杂度为O(1)的步骤.... }
该循环2^f(n)<=n; f(n)<=log2n 取最大值f(n)= log2n, T(n)=O(log2n)
四:循环嵌套复杂度分析
for(int count1 = 0; count1 < n; count1++){ for(int count2 = 0; count2 < n; count2++){ //一些列复杂度为O(1)的步骤.... } }
在这种情况下应该 先计算内层循环的时间复杂度,然后用内层的复杂度乘以外层循环的次数。 最内层循环体的时间复杂度都是O(1)所以循环n次也就是O(n) 在乘以最外层for的n次。所以得出结论 2层嵌套循环的时间复杂度 = O(1) * n*n = O(n2)
算法优化:
计算1 + 2 + 3 + 4 + ...... + n =? ,通过程序实现,并标出算法的时间复杂度。
Java解法一:
public int method01(int n){ int sum = 0; for(int i=1;i<=n;i++){ sum += i; } return sum; }
时间复杂度: O(n)
德国数学家高斯曾经在他约9岁的时候就提过自己的解法:
SUM = 1 + 2 + 3 + 4 + ...... + 100
SUM = 100 + 99 + 98 + 97 + ...... + 1
SUM + SUM = 2*SUM = 101 + 101 + 101 + .... + 101 //正好 100 个 101
SUM = (100*101)/2 = 5050
Java解法二:
public int method02(int n){ int sum = 0; sum = (n*(n-1))/2; return sum; }
时间复杂度: O(1)
从时间复杂度上来年,解法二比解法一更高效
常见的算法时间复杂度由小到大依次为:
Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<{Ο(2n)<Ο(n!)<O(nn)}
最后三项用大括号把他们括起来是想要告诉大家,如果日后大家设计的算法推导出的“大O阶”是大括号中的这几位,那么趁早放弃这个算法,在去研究新的算法出 来吧。因为大括号中的这几位即便是在 n 的规模比较小的情况下仍然要耗费大量的时间,算法的时间复杂度大的离谱,基本上就是“不可用状态”。
参考资料:
http://www.cnblogs.com/sakorn/archive/2007/05/18/751661.html
http://www.douban.com/note/91775206/
http://blog.csdn.net/xingqisan/article/details/3206303
http://www.cnblogs.com/yanng/articles/2178710.html