由于时间复杂度是为了表达在大规模下基础操作的执行次数,因此当获得关于问题规模n的函数 f(n) 时,我们只关注最高次幂,并且忽略其系数,因为这些值在大规模问题下并不能对其时间变化产生明显影响。如,,这里我们将忽略常数项,低次幂以及
的系数3,获得时间复杂度为
。
计算时间复杂度有2种方法,一种是根据直观的循环结构来计算,另一种是根据基础操作来推算。以下,分别阐述:
一.直观计算
public static void fc(int N)
{
int sum = 0;
for (int i = 1; i < N; i *= 2) { // 循环n次
for (int j = 0; j < N; j++) { // 循环n次
sum++;
}
}
System.out.println(sum);
}
上述代码共有2层循环,嵌套循环遵循乘法原则,所以时间复杂度为O()
二.规模计算
public static void fb(int N)
{
int sum = 0;
for (int i = 1; i < N; i *= 2) {
for (int j = 0; j < i; j++) {
sum++;
}
}
System.out.println(sum);
}
外层循环很容易看出是,但内层循环 j 是根据外层循环 i 的值变化的,无法直观计算,此时就需要根据问题规模来算时间复杂度。
先找到基础操作sum++,可以看出,当 i=1 时,sum++执行1次,当 i=2 时,sum++执行2次,当 i=4 时,sum++执行4次,...,当 i=N 时,sum++执行N次。根据加法原则,sum++总共执行次数应为,1+2+4+...+N次,根据等比数列求和公式 ,根据公式得出执行次数总和
(n为项数),又因为n和N的关系可以有如下表示:
,整理得
,将n和N的关系等式代入
中得出结果
,因此得到时间复杂度为O(N)。
三.练习题
1.分析以下算法的时间复杂度
void fun(int n)
{
int i=0,s=0;
while(s<n){
++i;
s=s+i;
}
}
答案:
i 的值随着循环次数增加,逐渐递增为1,2,3,4...,m
s的值实际为 i 的所有取值的总和,所以当i=m时,,由于循环条件为s<n,因此在循环第m次时,s+k(假设的一个常量)=n,即为
。
求根公式:
由求根公式得,,即
去掉常数项和n的系数,得出时间复杂度T(n)=
2.求最后一行的语句频度在最坏的情况下是多少。
for(i=n-1;i>=1;--i)
for(j=1;j<=i;++j)
if(A[j]>A[j+1]) A[j]与A[j+1]对换;
答案:
内层循环次数随着 i 的变化而变化,i=n-1时,内层循环n-1次;i=n-2时,j循环n-2次;...;i=2时,j循环2次;i=1时,j循环1次。(n-1,n-2,n-3,...,2,1)
根据加法原则,内层循环总次数的和为,,整理得,
去掉低次幂及高次幂的系数,得到时间复杂度,。
3.如下函数mergesort()执行的时间复杂度为多少?假设函数调用被写为mergesort(1,n),函数merge()的时间复杂度为O(n)。
void mergesort(int i,int j)
{
int m;
if(i!=j){
m=(i+j)/2; // O(1)
mergesort(i,m);
mergesort(m+1,j);
merge(i,j,m); // O(n)
}
}
答案:
因为m为 i,j 的平均数,可以得出,
即
...
又因为,当
时,为最终时刻,即为f(1)的时候,
代入得
所以时间复杂度为,。