数据结构杂谈番外篇——时间复杂度计算

举报
ArimaMisaki 发表于 2022/08/09 01:15:18 2022/08/09
【摘要】 我们先给出推导的方法,然后下面一步一步来推导。 推导大O阶 用常数1取代运行时间中的所有加法常数在修改后的运行次数函数中,只保留最高阶项如果最高阶存在且不是1,则去除这个项相乘的常数所得结果即为大O阶...

我们先给出推导的方法,然后下面一步一步来推导。

推导大O阶

  1. 用常数1取代运行时间中的所有加法常数
  2. 在修改后的运行次数函数中,只保留最高阶项
  3. 如果最高阶存在且不是1,则去除这个项相乘的常数
  4. 所得结果即为大O阶

示例

int sum = 0,n = 100; //以分号结尾,代码执行一次
sum = (1+n)*n/2 //执行一次
cont<<sum<<endl; //执行一次

  
 
  • 1
  • 2
  • 3

运行次数为3,而在上面推导的推导大O阶方法中我们说了,用1代码所有的加法,什么意思呢?我们的3是经过1+1+1算出来的,我们用1代替所有的加法。而这个表达式只有1,没有最高阶项,所以结果1即为大O阶。我们把具有O(1)的时间复杂度的叫做常数阶

int i;
for(i = 0;i<n;i++)
{
    /*其他常数阶程序代码*/
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

我们可以发现,花括号里的就是常数阶,而for循环循环了n次,也就是说,做了n+常数阶次执行次数,根据上面推导大O阶方法,我们找到了最高阶项n,舍弃后面常数项,所以我们的时间复杂度为O(n),我们把这类情况称为线性阶

顺便一提,一般来说,我们分析算法的时间复杂度,关键就是分析循环结构的运行情况

int count = 1;
while(count < n)
{
	count = count * 2;
	/*其他常数阶程序代码*/
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

从这里的代码我们可以看出,退出循环的条件是count<n,而count是通过自身乘2来更新自我然后跳出循环的。也就是说,设count更新次数为x,其可以写出 2 x = n 2^{x}=n 2x=n的式子,而我们大O(n)里面的n实际上指的是这里的x,根据高中数学所学的指对互换,我们可以写出 x = l o g 2 n x = log_2n x=log2n。所以这个循环的时间复杂度为O(logn),我们把这类情况叫做对数阶

int i ,j ; 
for (i - 0; i < n; i++) {
	for ( j - 0 ; j < n ; j++ )
    {
        /*时间复杂度为O(1)的程序步骤序列*/
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

对于这种就不必多说了,时间复杂度为O ( n 2 ) (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++;
	}
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

分析算法,根据推导大O阶方法,第一行执行1次,第一个循环执行n次,第一个内嵌循环外层n次,内层n次,也就是n的平方。把常数变为1,然后抓大头,最高项系数为1,那么只剩下 n 2 n^2 n2。所以该代码的时间复杂度为O ( n 2 ) (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;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

最复杂的就是中间的内嵌循环,外层循环为从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];
	}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

上面这个是一个N×N矩阵相乘的算法,连续三层循环,第一次执行次数为n,第二层执行次数也为n,第三层执行次数还是n。所以该题算法复杂度为O ( n 3 ) (n^3) (n3)

for(i = 1;i<=n;i++)
	for(j=1;j<=i;j++)
		for(k=1;k<=j;k++)
			x = x+1;

  
 
  • 1
  • 2
  • 3
  • 4

这里最外层执行次数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。如果听不太懂,我们可以用图来表示,即:

image-20220112191100363

所以实际上以上规律是由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 = 1 n i ( i + 1 ) 2 \sum^n_{i=1}\frac{i(i+1)}{2} i=1n2i(i+1)

我们化简一下上面的式子: ∑ i = 1 n ( i 2 2 + i 2 ) = 1 2 ∑ i = 1 n ( i 2 − 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)=21i=1n(i2i)

这里用等差求和公式带入求解,即可得出答案 n ( n + 1 ) ( n + 2 ) 6 \frac{n(n+1)(n+2)}{6} 6n(n+1)(n+2),根据我们前面说的大O阶推导,可以得出本题的时间复杂度为 n 3 n^3 n3

文章来源: blog.csdn.net,作者:ArimaMisaki,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/chengyuhaomei520/article/details/122782129

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。