1. 程式人生 > >遞歸算法時間復雜度分析與改善

遞歸算法時間復雜度分析與改善

一個 cci 存在 改善 遞歸實現 for 簡潔 water height

遞歸算法大家都不陌生,當須要反復計算同樣問題時,一般能夠選擇遞歸和循環兩種算法。

又由於遞歸實現起來代碼比較簡潔。所以通常都會使用遞歸來解決上述問題。比方斐波那契數列。再比方樹的前序、中序、興許遍歷算法。

遞歸算法盡管是有代碼簡潔這個長處,可是其缺點顯著。

由於遞歸函數是在運行過程中調用其自身,所以會占用大量的棧上空間,而且壓棧和出棧都是有時間消耗的。

所以從這一點上來看,遞歸的效率是不如循環。除了效率之外,遞歸另一個相當明顯的問題:可能會導致棧溢出。當遞歸調用的次數過大時,非常有可能會出現棧溢出的情況。

我們這裏暫不考慮空間復雜度,僅討論其時間復雜度以及改善方法。

還是以經典的Fibonacci數列為例。其定義例如以下:

技術分享

1. 遞歸解法

對於這個題目,大家對於其算法已經十分熟悉。非常快就能寫出以下的代碼:

long long Fibonacci(unsigned int n)
{
    if (n <= 0) {
        return 0;
    }

    if (n == 1) {
        return 1;
    }

    return Fibonacci(n-1) + Fibonacci(n-2);
}
我們以f(10)為例分析來分析遞歸的計算過程。f(10)=f(9)+f(8), f(9)=f(8)+f(7)
, f(8)=f(7)+f(6)。。

。。

能夠用樹形結構來表示整個計算過程

技術分享

我們能夠看出來,上面的樹中,非常多結點都是反復計算的。其實,遞歸算法的時間復雜度是n的指數級的。這種復雜度。一般來說是不可接受的。


2. 遞歸算法改善

上述的遞歸算法中。時間復雜度高的原因是過程中存在大量的反復計算。因此,假設能想辦法避免反復計算,那麽其時間復雜度便能夠降下來。

比較簡單的方法是採用逆序的遞歸算法:f(0)+f(1)=f(2), f(1)+f(2)=f(3),以此類推便能夠計算出f(n)。

而且這個算法的時間復雜度非常明顯,就是O(n)。代碼例如以下:

long long Fibonacci(unsigned int n)
{
    long long fibNMinusOne = 1;
    long long fibNMinusTwo = 0;
    long long fibN = 0;
    int result[2] = {0, 1};
    int i;

    if (n < 2) {
        return result[n];
    }
    
    for (i = 2; i < n; i++) {
        fibN = fibNMinusTwo + fibNMinusOne;
        fibNMinusTwo = fibNMinusOne;
        fibNMinusOne = fibN;
    }

    return fibN;
}

遞歸算法時間復雜度分析與改善