我们先给出推导的方法,然后下面一步一步来推导。
推导大O阶
- 用常数1取代运行时间中的所有加法常数
- 在修改后的运行次数函数中,只保留最高阶项
- 如果最高阶存在且不是1,则去除这个项相乘的常数
- 所得结果即为大O阶
示例
int sum = 0,n = 100; //以分号结尾,代码执行一次
sum = (1+n)*n/2 //执行一次
cont<<sum<<endl; //执行一次
运行次数为3,而在上面推导的推导大O阶方法中我们说了,用1代码所有的加法,什么意思呢?我们的3是经过1+1+1算出来的,我们用1代替所有的加法。而这个表达式只有1,没有最高阶项,所以结果1即为大O阶。我们把具有O(1)的时间复杂度的叫做常数阶
。
int i;
for(i = 0;i<n;i++)
{/*其他常数阶程序代码*/
}
我们可以发现,花括号里的就是常数阶,而for循环循环了n次,也就是说,做了n+常数阶
次执行次数,根据上面推导大O阶方法,我们找到了最高阶项n,舍弃后面常数项,所以我们的时间复杂度为O(n),我们把这类情况称为线性阶
。
顺便一提,一般来说,我们分析算法的时间复杂度,关键就是分析循环结构的运行情况
。
int count = 1;
while(count < n)
{count = count * 2;/*其他常数阶程序代码*/
}
从这里的代码我们可以看出,退出循环的条件是count<n,而count是通过自身乘2来更新自我然后跳出循环的。也就是说,设count更新次数为x,其可以写出2x=n2^{x}=n2x=n的式子,而我们大O(n)里面的n实际上指的是这里的x,根据高中数学所学的指对互换
,我们可以写出x=log2nx = log_2nx=log2n。所以这个循环的时间复杂度为O(logn),我们把这类情况叫做对数阶
。
int i ,j ;
for (i - 0; i < n; i++) {for ( j - 0 ; j < n ; j++ ){/*时间复杂度为O(1)的程序步骤序列*/}
}
对于这种就不必多说了,时间复杂度为O(n2)(n^2)(n2)。我们把这类情况叫做平方阶
。
说完上面所有的情况了,现在我们来几个题来练手。
x = 0;y = 0;
for(int k = 0;k<n;k++){x++;
}
for(int i = 0;i<n;i++){for(int j = 0;j<n;j++){y++;}
}
分析算法,根据推导大O阶方法,第一行执行1次,第一个循环执行n次,第一个内嵌循环外层n次,内层n次,也就是n的平方。把常数变为1,然后抓大头,最高项系数为1,那么只剩下n2n^2n2。所以该代码的时间复杂度为O(n2)(n^2)(n2),从完整的代码分析下来我们也可以发现,实际上我们只需要找最复杂的那个循环开始分析就可以了,因为其他的代码所含的时间复杂度最终根据推导大O阶方法都会被省略。下面看一个比较难的例子。
void exam(fload x[][],int m,int n)
{float sum[];for(int i = 0;i<m;i++){sum[i] = 0.0;for(int j = 0;j<n;j++){sum[i]+=x[i][j];}}for(i = 0;i<m;i++)cout<<i<<":"<<sum[i]<<endl;
}
最复杂的就是中间的内嵌循环,外层循环为从0到m,内层循环0到n,所以该时间复杂度应为O(m+n)。
for(i = 1;i<=n;i++)for(j = 1;j<=n;j++){c[i][j]=0;for(k = 1;k<=n;k++)c[i][j] = c[i][j]+a[i][k]*b[k][j];}
上面这个是一个N×N矩阵相乘的算法,连续三层循环,第一次执行次数为n,第二层执行次数也为n,第三层执行次数还是n。所以该题算法复杂度为O(n3)(n^3)(n3)。
for(i = 1;i<=n;i++)for(j=1;j<=i;j++)for(k=1;k<=j;k++)x = x+1;
这里最外层执行次数n,但是最外层执行1次,第二层就循环1次;最外层执行第2次,第二层循环两次;根据等差数列求和公式,即第二层有1+2+3+…+n,即n(1+n)2\frac{n(1+n)}{2}2n(1+n)次。当然第三层就不好理解了,所以我们接下来换一种方法。
对于三层循环问题,我们还是直接列出最外层的前几项比较好。在i = 1的时候,内层循环全部加起来只循环一次。在i = 2的时候,第二层循环启动两次循环,所以总共执行1+2,对于i = 3,第二层启动三次循环,第三层也是三次循环。也就是说总共执行1+2+3。如果听不太懂,我们可以用图来表示,即:
所以实际上以上规律是由n来控制的,从上面的图来看的话,根据我们所得规律,i=1里面有一个i(1+i)2\frac{i(1+i)}{2}2i(1+i),i = 2里面也有一个i(1+i)2\frac{i(1+i)}{2}2i(1+i),以此类推我们可以写出下面的式子:∑i=1ni(i+1)2\sum^n_{i=1}\frac{i(i+1)}{2}∑i=1n2i(i+1)。
我们化简一下上面的式子:∑i=1n(i22+i2)=12∑i=1n(i2−i)\sum^n_{i=1}(\frac{i^2}{2}+\frac{i}{2}) = \frac1 2\sum^n_{i=1}(i^2-i)∑i=1n(2i2+2i)=21∑i=1n(i2−i)。
这里用等差求和公式带入求解,即可得出答案n(n+1)(n+2)6\frac{n(n+1)(n+2)}{6}6n(n+1)(n+2),根据我们前面说的大O阶推导,可以得出本题的时间复杂度为n3n^3n3。