遞歸算法大家都不陌生,當須要反復計算同樣問題時,一般能夠選擇遞歸和循環兩種算法。
又由於遞歸實現起來代碼比較簡潔。所以通常都會使用遞歸來解決上述問題。比方斐波那契數列。再比方樹的前序、中序、興許遍歷算法。
遞歸算法盡管是有代碼簡潔這個長處,可是其缺點顯著。
由於遞歸函數是在運行過程中調用其自身,所以會占用大量的棧上空間,而且壓棧和出棧都是有時間消耗的。
所以從這一點上來看,遞歸的效率是不如循環。除了效率之外,遞歸另一個相當明顯的問題:可能會導致棧溢出。當遞歸調用的次數過大時,非常有可能會出現棧溢出的情況。
我們這里暫不考慮空間復雜度,僅討論其時間復雜度以及改善方法。
還是以經典的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; }