它通常是计算程序执行过程中赋值和比较操作的次数。一般只考虑赋值情况。
考虑最好、平均和最坏的情况。
例a.对数组中所有元素求和
for(i=0,sum=0;i<n; i++)
sum+=a[i];
分析:
先初始化两个变量i和sum,然后执行n次for循环,每次循环中,都会进行两次的赋值(sum,i++),循环结束后一共进行了2+2n次赋值,因此这个算法的渐近复杂度为O(n)。
例b.嵌套循环
for(i=0;i<n;i++)
for(j=1,sum=a[0];j<=i;j++)
sum+=a[j];
分析:
先初始化i,然后外循环执行n次,在外层循环的每次迭代中都执行内层的for循环,给j、i、sum赋值,对于每一个i,内层循环都会执行i次,在每次的迭代中,都会进行两个赋值,一个是对sum,一个是对j赋值,因此程序总共进行了次。
例c.二分查找算法分析
int binarySearch(int a[], int len, int key)
{
int low=0,high=len-1;
int mid;
while(low<high)
{
mid=(low+high)/2;
if(a[mid]<key)
low=mid+1;
else if(a[mid]>key)
high=mid-1;
else
return mid;
}
return -1;
}
分析:
如果key正好位于数组的正中间,则循环只执行一次;如果key不在数组中,如何考虑?首先数组长度为n,数组的一半是n/2,一半的一半是n/,以此类推直到数组的长度为1。于是得到数列n,n/2,n/…,n/,只要知道m的值就好,n/2m=1,得到m=lgn。
例d.考虑递归情况
1. 递归实现机制
每一次递归调用,都用一个特殊的数据结构"栈"记录当前算法的执行状态,特别地设置地址栈,用来记录当前算法的执行位置,以备回溯时正常返回。递归模块的形式参数是普通变量,每次递归调用得到的值都是不同的,他们也是由"栈"来存储。
2. 递归算法效率分析方法
递归算法的分析方法比较多,最常用的便是迭代法。
迭代法的基本步骤是先将递归算法简化为对应的递归方程,然后通过反复迭代,将递归方程的右端变换成一个级数,最后求级数的和,再估计和的渐进阶。
递归方程有如下几种形式:
(a)递归方程为: T(n) = T(n - 1) + O(1); 这个例子的时间复杂性是线性的。
迭代展开: T(n) = T(n - 1) + O(1)
= T(n - 2) + O(1) + O(1)
= T(n - 3) + O(1) + O(1) + O(1)
= ......
= O(1) + ... + O(1) + O(1) + O(1)
= n * O(1)
= O(n)
(b) 递归方程:T(n) = 2T(n/2) + 2, 且假设n=2的k次方。 时间复杂度是线性的
迭代展开:T(n) = 2T(n/2) + 2
= 2(2T(n/2*2) + 2) + 2
= 4T(n/2*2) + 4 + 2
= 4(2T(n/2*2*2) + 2) + 4 + 2
= 2*2*2T(n/2*2*2) + 8 + 4 + 2
= ...
= 2k+2k-1+2k-2+…+2
=2 * n - 2
= O(n)
(c)递归方程: T(n) = 2T(n/2) + O(n), 且假设n=2的k次方。
迭代展开: T(n) = 2T(n/2) + O(n)
= 2*2T(n/4) + 2O(n/2) + O(n)
=2*2*2T(n/8) + 2*2O(n/4)+2O(n/2) + O(n)
= ...
= O(n) + O(n) + ... + O(n) + O(n) + O(n)
= k * O(n)
= O(k*n)
= O(nlog2n) //以2为底
一般地,当递归方程为T(n) = aT(n/c) + O(n), T(n)的解为:
O(n) (a<c && c>1)
O(nlog2n) (a=c && c>1) //以2为底
O(nlogca) (a>c && c>1) //n的(logca)次方,以c为底
上面介绍的3种递归调用形式,比较常用的是第一种情况,第二种形式也有时出现,而第三种形式(间接递归调用)使用的较少,且算法分析 比较复杂。 下面举个第二种形式的递归调用例子。
(d)递归方程为:T(n) = T(n/3) + T(2n/3) + n
为了更好的理解,先画出递归过程相应的递归树:
n --------> n
n/3 2n/3 --------> n
n/9 2n/9 2n/9 4n/9 --------> n
...... ...... ...... ....... ......
--------
总共O(nlogn)
累计递归树各层的非递归项的值,每一层和都等于n,从根到叶的最长路径是:
n --> (2/3)n --> (4/9)n --> (12/27)n --> ... --> 1
设最长路径为k,则应该有:
(2/3)的k次方 * n = 1 得到 k = log(2/3)n // 以(2/3)为底
于是 T(n) <= (K + 1) * n = n (log(2/3)n + 1)
即 T(n) = O(nlogn)
由此例子表明,对于第二种递归形式调用,借助于递归树,用迭代法进行算法分析是简单易行的。