大O定义(Big O notation)
描述算法所用时间随着输入变量变化的时间增长关系(这里是非严谨的定义,如果需要数学严谨性的定义,请参考《算法概论》里面的定义。
就是给了一个输入N,时间会随着N怎么变化,这个N是输入参数,O(N)和O(x)或者O(y)没有区别。这里的时间会随着CPU等硬件而不同,这里我们其实强调的是算法的步骤,来代替实际的时间。
其实O(N)表示最坏的情况下用的时间,Ω(N)表示最好的情况下用的时间。θ(N)表示平均时间。这里只讨论O(N)
大O的计算原则
1.常量是被忽略的,如14n² ,直接变成n²
2.忽略低次幂,如n²+n,直接变成n²。因为n²+n <= n²+n²<=2n² ,根据第一条,常数忽略为n²
3.n在指数位置,则忽略n的底数位置。如3n+n5,则忽略n5,直接变成3n
4.n的多项式,则忽略n的对数,如n+(logn)3 直接写成n
这样我们就关注算法的重要影响部分。
时间复杂度举例
1. 斐波那契数列,伪代码
function fib1(n)
if n = 0: return 0
if n = 1: return 1
return fib1(n - 1) + fib1(n - 2)
分析:对于n<=1,时间复杂度为O(1),对于n>1 ,用时有O(n) = O(n-1)+O(n-2)[二阶齐次差分方程,具体百度] =O(2n),可以看出,递归还是很耗时的。因为他每一次都是往下分了2个调用,时间是2×2×2...如下图:
2.冒泡排序
function bubble_sort (array, n) {
var i, j;
for(i from 0 to n-1){
for(j from 0 to n-1-i){
if (array[j] > array[j+1])
swap(array[j], array[j+1])}}
}
分析:外层是n,内层是(n-1+n-2+n-3+...+3+2+1),所以是n*(n/2)=O(n²/2),忽略常数1/2,记作O(n²).当然这是最坏的情况,最好的情况是O(n),加个flag,标识是否交换,没有交换直接结束了。
3.二分查找
BinarySearch_Left(A[0..N-1], value) { low = 0 high = N - 1 while (low <= high) { // invariants: value > A[i] for all i < low value <= A[i] for all i > high mid = (low + high) / 2 if (A[mid] >= value) high = mid - 1 else low = mid + 1 } return low }
分析:查找的次数---经过多少次(除以二)最后数字变成1,设经过x次,有:1 = N / 2x,则x=log2 N,所以算法复杂度是O(log2 N).
End