今天看《劍指Offer》看到了斐波那契數列其實用遞歸調用樹的話會有很多重復計算......(之前自己就是一直圖省事,感覺寫的代碼少,用遞歸很方便,但是仔細一想的確是有很多計算是重復的,浪費了很多時間所以今天的隨筆就說一下斐波那契的順序實現吧
一、常見的遞歸實現
int Fibonacci(int num)
{
if(num == 0 || num == 1) return num;
return Fibonacci(num-1)+Fibonacci(num - 2)
}
以前的我一直是用遞歸寫的,因為可以看到,真的很方便(這里寫的簡單了,沒有考慮負數和異常輸入之類的,自己編寫時還是要多加考慮邊界條件和異常輸入啦~)
我們可以看出比如Num = 4時 要算Num = 3時的值 + Num = 2的值,而Num = 3時又要算一遍Num = 2的值,就很累贅。所以用順序結構從1到n這樣的話,就能少掉這個麻煩。
二、兩種順序實現
第一種方法是用兩個數不斷變換,最終得到目標值
/*順序斐波那契額*/
#include<iostream>
int Fibonacci(int n){
int sum = 0,tmp = 0;
int num1 = 0,num2 = 1;
if(n == 0 || n == 1) tmp = n;
for(int i=0;i<=n-2;i++)
{
tmp = num1 + num2;
num1 = num2;
num2 = tmp;
}
return tmp;
}
int main()
{
int n;
while(std::cin>>n)
{
std::cout<<"fnums is :"<<Fibonacci(n)<<"\n";
}
return 0;
}
這一個方法還可以用來尋找一組數的最大值和次大值,也是循環一遍的O(n)還是很方便的。
第二種是使用隊列實現
#include<iostream>
#include<queue>
int Fibonacci(int n)
{
if(n==0 || n==1)
return n;
std::queue<int> q;
q.push(0);
q.push(1);
while(!q.empty() && n >= 2)
{
int x = q.front();
q.pop();
int y = q.front();
q.push(x+y);
n--;
}
q.pop();
int result = q.front();
q.pop();
return result;
}
int main()
{
int n;
while(std::cin>>n)
{
std::cout<<"fnums is :"<<Fibonacci(n)<<"\n";
}
return 0;
}
當然還有一種方法是數組實現啦~我覺得用的空間會大於第一種方式,而且原理也並沒什么差別在此就不寫啦~
總之呢,遞歸雖然代碼量少,(可是代碼量少並不是決定一個程序員的強弱啊喂!)但是重復計算過程有點點多,然后可憐的棧也會出現STACK OVERFLOW(棧溢出)的錯誤,所以還是順序比較好嘛~隨着n的增大,順序的優勢也會更加明顯的~
在《劍指Offer》上還說了需要知識遷移的能力,提到了一只青蛙一次可以跳上1級台階,也可以跳上兩級台階,問青蛙跳上n級台階總共有幾種跳法的問題.
這類問題可以這樣想,1級台階有1種,2級台階有兩種,我們可以知道上3級台階前,青蛙一定是站在n-2(1)級台階或者上n-1(2)級台階,所以就是把F(n-2)+F(n-1)加在一起就是上n級台階的跳法總數啦,沒錯,就是你!斐波那契數列!
和這個思維很像的還有漢諾塔問題,要把柱子A的N個圓盤放到C,也就是把柱子A的N-1個圓盤放到B,然后再把第N個圓盤放在C,最后把B柱子通過A放到C,也就是2*F(N-1)+1