這也是人們最容易忽視的地方,有些時候使用遞歸是比較簡單,但卻可能付出很大的負擔!
相反我們可以考慮另一種形式“循環迭代”----有些時候是遞歸的良好替代算法!但也不盡然!
上次我們講解了遞歸的使用條件----存在一個遞歸調用的終止條件;每次遞歸的調用必須
越來越靠近這個條件;只有這樣遞歸纔會終止,否則是不能使用遞歸的!
好了下面我們也以人們經常使用的兩個例子來講解:階乘和菲波那契數組
我們先來看看階乘的計算:
/
| 1 ,n<=0;
factorial(n)=|
|n*factorial(n-1) n > 0;
\
給出這種結果很容易讓人考慮使用遞歸!
很明顯這個例子符合我們前面講的遞歸的使用條件,下面我們就用遞歸算法來實現階乘的計算
/*使用遞歸來計算階乘*/
long
factorial(int n)
{
if(n > 0)
return n * factorial(n - 1);
else
return 1;
}
我們再來一個我們自己的完全使用循環迭代來實現的方法
/*使用循環的階乘計算方法*/
long
factorial(int n)
{
int result = 1;
while ( n > 1){
result *= n ;
n--;
}
return result;
}
對比上面這兩個程序我們會發現使用迭代有一個嚴重的缺陷----開銷太大!
每次計算參數必須壓棧,爲局部變量分配內存空間,寄存器的值必須保存等,
當遞歸調用返回是還需要還原上面的操作!所以開銷是很大的,在這個計算中
相比而言我們是不會使用階乘的!使用循環迭代無疑會帶來更高的效率!
/
| 1 ,n<=1;
fibonacci(n)=| 1 ,n=2;
|fibonacci(n - 1) + fibonacci(n - 2) ,n > 0;
\
這個相信大家都不陌生了----菲波那契數組,他的定義也容易讓我使用數組來解決
但稍後你將感受到你將付出多大的代價來使用這種遞歸!
我們來考慮一下,假設現在要計算fibonacci(n -1)和fibonacci(n -2),請注意我們
在計算前者的時候就已經將後者計算出來了,但卻無法使用,還必須重新計算!
他的代價遠不止這樣一個冗餘的計算:每個遞歸調用都將觸動另外兩個遞歸調用,而這兩個
當中的任何一個同樣也會再觸發另外兩個遞歸調用.........
每次都會有大量的計算只被使用一次,卻還要從新計算很多次。在這個例子中的開銷是相當
大的!
/*利用遞歸計算第n 個菲波那契數*/
long
fibonacci(int n )
{
if(n <= 2)
return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
/*使用循環迭代方法完成第n個菲波那契數的計算*/
long
fibonacci(int n)
{
long result;//計算結果
long previous_first_result;//前面的一個數
long previous_second_result;//前面的第二個數
result = previous_first_result = 1;
while ( n > 2){
n--;
previous_second_result = previous_first_result;
previous_first_result = result;
result = previous_first_result + previous_second_result;
}
return result;
}
很多問題我們使用遞歸的形式解釋,可以將問題解釋的更清楚,但在實現時不一定
非要使用遞歸來實現,因爲遞歸未必是最好的方法!
總之,在你使用遞歸來處理問題之前必須首先考慮使用遞歸帶來的好處是否能補償
他所帶來的代價!