时间复杂度的计算

时间复杂度的计算

概要说明

本文主要简单记录自己在学习数据结构的算法复杂度的过程中,遇到的问题和一些心得体会,便于以后自己参考。

时间复杂度的定义

一个语句的频度是指该语句在算法中被重复执行的次数。算法中所有语句的频度之和记为T(n),它是该算法问题规模 n 的函数,时间复杂度主要分析 T(n)

的数量级。算法中基本运算 (最深层循环内的语句)的频度与 T(n)同数量级,因此通常采用算法中基本运算的频度f(n))来分析算法的时间复杂度: T(n)=O(f(n))

加法规则

T ( n ) = T 1 ( n ) + T 2 ( n ) = O ( f ( n ) ) + O ( g ( n ) ) = O ( m a x ( f ( n ) , g ( n ) ) ) T(n) = T_1(n) + T_2(n) = O(f(n)) + O(g(n)) = O(max(f(n), g(n))) T(n)=T1(n)+T2(n)=O(f(n))+O(g(n))=O(max(f(n),g(n)))

乘法规则

T ( n ) = T 1 ( n ) × T 2 ( n ) = O ( f ( n ) ) × O ( g ( n ) ) = O ( f ( n ) × g ( n ) ) T(n) = T_1(n) \times T_2(n) = O(f(n)) \times O(g(n)) = O(f(n) \times g(n)) T(n)=T1(n)×T2(n)=O(f(n))×O(g(n))=O(f(n)×g(n))

常见的渐进时间复杂度

O ( 1 ) < O ( l o g ) 2 n < O ( n ) < O ( n l o g 2 n ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) < O ( n ! ) < O ( n n ) O(1) < O(log)_2^n < O(n) < O(nlog_2^n) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n) O(1)<O(log)2n<O(n)<O(nlog2n)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)

计算时间复杂度的2种通用方法

1 循环主体中的变量参与循环条件的判断

int i = 1;
while(i < n)
    i=i*2;

假设第t次退出循环,每次循环i乘以2,第t次,就是 2 t 2^t 2t,那么有 2 t < n 2^t < n 2t<n,即是: t < = l o g 2 n t<= log_2^n t<=log2n,从而时间复杂度是 O ( l o g 2 n ) O(log_2^n) O(log2n)

2 循环主体的变量和循环条件无关

此类题可采用数学归纳法或直接累计循环次数。多层循环时从内到外分析,忽略单步语句、条件判断语句,只关注主体语句的执行次数。此类问题又可分为递归程序和非递归程序。递归程序一般使用公公式递推,非递归程序直接累计计数

例题 1

一个算法所需时间由下述递归方程表示,试求出该算法的时间复杂度的级别(或阶)
T ( n ) = { 1 n = 1 2 T ( n / 2 ) + n n > 1 T(n) = \begin{cases} 1 & n = 1 \\ \\ 2T(n/2) + n & n > 1 \end{cases} T(n)= 12T(n/2)+nn=1n>1

式中, n n n是问题的规模,为简单起见,设 n n n是2的整数次幕.

解答如下:

n = 2 k n=2^k n=2k,其中 k > = 0 k>= 0 k>=0, 则有
T ( 2 k ) = { 1 k = 0 2 T ( 2 k − 1 ) + 2 k k > 0 T(2^k) = \begin{cases} 1 & k = 0 \\ \\ 2T(2^{k-1}) + 2^k & k > 0 \end{cases} T(2k)= 12T(2k1)+2kk=0k>0
从而,当 k > 0 k>0 k>0时, T ( 2 k ) = 2 T ( 2 k − 1 ) + 2 k T(2^k) = 2T(2^{k-1}) + 2^k T(2k)=2T(2k1)+2k;用k取k-1得到:
T ( 2 k − 1 ) = 2 T ( 2 k − 2 ) + 2 k − 1 T(2^{k-1}) = 2T(2^{k-2}) + 2^{k-1} T(2k1)=2T(2k2)+2k1
( 2 ) (2) 2式带入 ( 1 ) (1) 1有:

T ( 2 k ) = 2 ( 2 T ( 2 k − 2 ) + 2 k − 1 ) + 2 k = 2 × 2 T ( 2 k − 2 ) + 2 × 2 k − 1 + 2 k = 2 × 2 T ( 2 k − 2 ) + 2 × 2 k = 2 2 × T ( 2 k − 2 ) + 2 × 2 k \begin{align} T(2^k) & = \begin{aligned} & 2(2T(2^{k-2}) + 2^{k-1}) + 2^k \\ \end{aligned} \\ & = 2\times2T(2^{k-2}) + 2\times2^{k-1} + 2^k \\ & = 2\times2T(2^{k-2}) + 2\times2^k \\ & = 2^2\times T(2^{k-2}) + 2 \times2^k \end{align} T(2k)=2(2T(2k2)+2k1)+2k=2×2T(2k2)+2×2k1+2k=2×2T(2k2)+2×2k=22×T(2k2)+2×2k
同样的,用k取k-2,并带入,得到:


T ( 2 k ) = 2 ( 2 T ( 2 k − 2 ) + 2 k − 1 ) + 2 k = 2 × 2 T ( 2 k − 2 ) + 2 × 2 k − 1 + 2 k = 2 × 2 T ( 2 k − 2 ) + 2 × 2 k = 2 2 × T ( 2 k − 2 ) + 2 × 2 k = 2 2 × ( 2 T ( 2 k − 2 + 2 k − 2 ) ) + 2 k = 2 3 × T ( 2 k − 3 ) + 2 2 × 2 k − 2 + 2 k = 2 3 × T ( 2 k − 3 ) + 3 × 2 k \begin{align} T(2^k) & = \begin{aligned} & 2(2T(2^{k-2}) + 2^{k-1}) + 2^k \\ \end{aligned} \\ & = 2\times2T(2^{k-2}) + 2\times2^{k-1} + 2^k \\ & = 2\times2T(2^{k-2}) + 2\times2^k \\ & = 2^2\times T(2^{k-2}) + 2 \times2^k \\ & = 2^2 \times ( 2T(2^{k-2} + 2^{k-2}) ) + 2^k \\ & = 2^3 \times T(2^{k-3}) + 2^2 \times 2^{k-2} + 2^k \\ & = 2^3 \times T(2^{k-3}) + 3 \times 2^k \end{align} T(2k)=2(2T(2k2)+2k1)+2k=2×2T(2k2)+2×2k1+2k=2×2T(2k2)+2×2k=22×T(2k2)+2×2k=22×(2T(2k2+2k2))+2k=23×T(2k3)+22×2k2+2k=23×T(2k3)+3×2k

观察,猜测 T ( 2 k ) = 2 i T ( 2 k − i ) + i × 2 k T(2^k) = 2^iT(2^{k-i}) + i\times2^k T(2k)=2iT(2ki)+i×2k,由于 T ( 1 ) = 1 T(1) = 1 T(1)=1,得到 , T ( 2 k ) = ( 1 + k ) × 2 k T(2^k) = (1+k) \times 2^k T(2k)=(1+k)×2k。用数学归纳法证明:

当k = 0时, T ( 1 ) = ( 1 + 0 ) × 2 0 = 1 T(1) = (1+0) \times 2^0 = 1 T(1)=(1+0)×20=1,成立,假设 k = m , m > 0 k=m, m> 0 k=m,m>0时,结论成立,则有: T ( 2 m ) = ( 1 + m ) × 2 m T(2^m) = (1+m) \times 2^m T(2m)=(1+m)×2m.

事实上, 当 k = m + 1 k=m+1 k=m+1时,有:

T ( 2 m + 1 ) = 2 × T ( 2 m ) + 2 m + 1 = 2 × ( ( 1 + m ) × 2 m ) + 2 m + 1 = ( 1 + m ) × 2 m + 1 + 2 m + 1 = ( 1 + ( 1 + m ) ) × 2 m + 1 = T ( m + 1 ) \begin{align} T(2^{m+1}) & = \begin{aligned} & 2 \times T(2^m) + 2^{m+1} \end{aligned} \\ & = 2 \times ( (1+m) \times 2^m ) + 2^{m+1} \\ & = (1+m) \times 2^{m+1} + 2^{m+1} \\ & = (1 + (1 +m)) \times 2^{m+1} \\ & = T(m+1) \end{align} T(2m+1)=2×T(2m)+2m+1=2×((1+m)×2m)+2m+1=(1+m)×2m+1+2m+1=(1+(1+m))×2m+1=T(m+1)
从而猜测的结论成立. 将 k = l o g 2 n k = log_2^n k=log2n带入,得到:

T ( n ) = { 1 n = 1 n + n l o g 2 n n > 1 T(n) = \begin{cases} 1 & n=1 \\ \\ n + nlog_2^n & n > 1 \end{cases} T(n)= 1n+nlog2nn=1n>1
于是时间复杂度为: O ( n ) + O ( n l o g 2 n ) O(n) +O(nlog_2^n) O(n)+O(nlog2n) ,取二者较大值: O ( n l o g 2 n ) O(nlog_2^n) O(nlog2n)

例题 2

求解斐波那契数列
F ( n ) = { 1 n = 0 , 1 F ( n − 1 ) + F ( n − 2 ) n > 1 F(n) = \begin{cases} 1 & n=0,1 \\ \\ F(n-1) + F(n-2) & n > 1 \end{cases} F(n)= 1F(n1)+F(n2)n=0,1n>1
有2种常用的算法:递归算法和非递归算法。试分析这两各种算法的时间复杂度。

  1. 递归算法
    递归算法是最简单的方法,但它具有较高的时间复杂度,因为它会重复计算相同的子问题,导致指数级的递归调用。

递归算法的递归关系是:
F ( n ) = F ( n − 1 ) + F ( n − 2 ) F(n) = F(n-1) + F(n-2) F(n)=F(n1)+F(n2)

递归算法的时间复杂度可用递归树来分析。每次递归调用都会分解成两个子问题,每个子问题又会分解成两个子问题,依此类推,直到 n 达到 0 或 1 时停止递归。因此,递归树的深度是 n,而每层的节点数是指数级增长的,约为 2 n 2^n 2n

因此,递归算法的时间复杂度是指数级的,大约为 O ( 2 n ) O(2^n) O(2n)。这使得递归算法在计算较大的斐波那契数时非常低效。

  1. 非递归算法(迭代算法)
    非递归算法采用迭代的方式来计算斐波那契数列,通过保存中间结果来避免重复计算,因此具有更低的时间复杂度。

非递归算法通过从小到大逐个计算斐波那契数,从 F ( 0 ) F(0) F(0) 和$ F(1) $开始,一直计算到 F ( n ) F(n) F(n)。这个过程只需要一次遍历,每次计算都只涉及前两个斐波那契数,因此时间复杂度是线性的,约为 O ( n ) O(n) O(n)

总结:

  • 递归算法的时间复杂度为 O ( 2 n ) O(2^n) O(2n),指数级增长,非常低效。
  • 非递归算法的时间复杂度为 O ( n ) O(n) O(n),线性增长,效率更高。

因此,通常情况下,推荐使用非递归算法来计算斐波那契数列,特别是在需要计算大数值时,因为它具有更高的效率。递归算法只在教育和理论分析中常常使用,而在实际应用中通常不适用。

总结

计算一个程序的事件复杂度,主要从2方面下手,看循环变量影不影响循环,如果影响,那就从这里入手,直接计算程序的运行次数;如果不影响,那就涉及到递归,需要观察得到递归的通式,来得出程序的时间复杂度。

参考资料

王道 数据结构复习

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值