再谈时间复杂度与空间复杂度

再谈时间复杂度与空间复杂度

在我的博客算法分析的一般方法中提到了时间复杂度,这篇博客不细谈空间复杂度,简单了解一下时间复杂度与空间复杂度之间的关系,以递归方法求解斐波那契数为例。斐波那契数形式如下:

\[0,1,1,2,3,5,8,13,21,\dots \]

代码如下:

long fib( int n )
{
    if( n <= 1 )
        return 1;
    else
        return fib( n - 1 ) + fib( n - 2 );
}

首先计算时间复杂度,当输入n为0或1时,时间是常数,所以

\[T( 0 ) = 1, \\ T( 1 ) = 1 \]

如果n大于或等于2,则要执行fib( n - 1 ) + fib( n - 2 );语句,按照递归定义,则:

\[T( N - 1 ) = fib( N - 1 ), \\ T( N - 2 ) = fib( N - 2 ) \]

所以整个斐波那契数的时间计算式为

\[T( N ) = T( N - 1 ) + T( N - 2 ) + 2, \\ T( 0 ) = 1, \\ T( 1 ) = 1 \]

根据归纳法可以证明斐波那契数:

\[F_k < (\frac{ 3 }{ 2 })^k \]

这意味着如下式子成立:

\[fib( N ) \ge ( \frac{ 3 }{ 2 } )^N,\quad N > 4 \]

结合上述时间计算式,可得

\[T( N ) \ge 2^N \]

这说明递归求解斐波那契数的时间复杂度是\(O( 2^N)\),是指数级增长。


空间复杂度的计算要容易得多,一般函数是在栈中进行(除非使用std::function等来动态地将函数放入堆中进行)。因为是在栈中进行的,一个栈帧占用一个空间,所以递归式的空间复杂度总的计算式:

\[S( N ) = N \times R( 1 ) \]

式中\(N\)表示递归深度,\(R( 1 )\)表示单次递归所用空间,它们相乘自然也就是递归式的空间复杂度。在递归法求解斐波那契数的空间复杂度也就很容易计算为\(O( N )\)。因为这个函数单次被调用所占据的空间在常规栈帧的大小范围内。


对于同一个算法,其时间复杂度与空间复杂度差别可能很大。一个程序通常要求输入再给出输出,时间复杂度与空间复杂度一般也是由输入联系起来,但这并不意味着二者是同步的(要么复杂度都复杂,要么都简单)。一般惯性思维会认为时间复杂度复杂则空间复杂度复杂,这是因为时间复杂度的基础是计算量,时间复杂度复杂则意味着计算量大,计算量大通常有很多数据需要加以计算从而说明有很多数据占据很多空间,也就得出空间复杂度复杂。本例是时间复杂度复杂而空间复杂度简单,这是因为这个递归算法存在重复计算,当存在重复计算时,则有较大差别。


小结

在程序设计中,时间复杂度与空间复杂度往往要做一定取舍,算法并非万能,很多时候要根据需要选择,通常会有两难境地——保时间舍空间或保空间舍时间。

posted @ 2025-07-01 23:39  永恒圣剑  阅读(10)  评论(0)    收藏  举报