題目1:寫一個函數,輸入n,其斐波那契數列的第n項。
斐波那契數列的定義如下:
方法1:使用遞歸解,時間復雜度是n的指數級別
斐波那契數列的定義就是遞歸的,我們根據定義可以很簡單的寫出代碼。代碼如下:

#include<iostream> #include<stdlib.h> using namespace std; //f(n)={0,1,1,2,3...} n>=0 int Fibonacci(int n) { if(n<=0) return 0; if(n==1) return 1; return Fibonacci(n-1)+Fibonacci(n-2); } void main() { int f=Fibonacci(30); cout<<f<<endl; system("pause"); }
但是這樣的方法存在明顯的不足,該方法的時間復雜度是n的指數級別,隨着n的增大,運算時間不可想象,比如說f(50)就要很久。時間復雜度之所以這么大,是因此計算過程中存在着重復計算。以f(10)為例,f(10)=f(9)+f(8),f(9)=f(8)+f(7)。其中的f(8)就是重復計算的。
方法2:開辟一個長度為(n+1)的數組,時間復雜度為O(n),空間復雜度為O(n)
前面我們計算斐波那契數列是從后往前計算的,就是計算f(n)=f(n-1)+f(n-2),然后再遞歸計算f(n-1),又是從后往前計算,就是因為這樣的從后往前計算,所以才會有很多的重復計算。那么我們可以逆轉思路,考慮從前往后計算。比如我們要計算f(4),那么我們就計算f(0)、f(1)、f(2)、f(3),將這些計算出來的值保存在一個數組arry[n+1]上,這樣計算斐波那契數列就相當於是一個填表的過程。時間復雜度大大降低。代碼實例如下:

int Fibonacci(int n) { if(n<=0) return 0; else if(n==1) return 1; else { //動態創建一個長度為(n+1)的數組 int *arry=new int[n+1]; arry[0]=0; arry[1]=1; for(int i=2;i<=n;i++) { arry[i]=arry[i-1]+arry[i-2]; } int result=arry[n]; //因為動態創建的數組不會因為出了作用域,內存就會被釋放。 //動態分配的數組將一直存在,直到程序顯示釋放它為止,因此這里使用delete [] //c++提供delete []表達式釋放指針所指向的數組空間。 delete [] arry; return result; } }
注意點:
- 因為不知道要求的f(n)中的n有多大,因此不能事先開辟一個數組,需要動態創建數組。而動態數組與數組變量不同,動態分配的數組將一直存在,直到程序顯式釋放它為止。普通的數組變量,只要出了數組的作用於,其內存會自動釋放。
- c++提供delete []表達式釋放指針所指向的數組空間。delete [] arry;該語句回收了arry所指向的數組,把相應的內存返回給自由存儲區。在關鍵字delete和指針arry之間的方括號[]是必不可少的:它告訴編譯器該指針指向的是自由存儲區中的數組,而非單個對象。delete arry只釋放了arry指針所指向的內存地址,理論上來說會少釋放了內存空間,從而產生內存泄露。
方法3:優化方法2,空間復雜度為O(1),時間復雜度為O(n)
在方法2中,我們保存了每一個中間變量,但是仔細觀察我們可以發現沒有必要保存每一個中間變量,我們只需要保存兩個臨時變量即可完成斐波那契數列的計算。具體代碼試下如下:

int Fibonacci(int n) { if(n<=0) return 0; else if(n==1) return 1; else { //當n>=2時,初始化pre=f(0)=0,post=f(1)=1,f(n)=0; int pre=0; int post=1; int fn=0; //采用循環計算斐波那契數列,通過兩個臨時變量pre和post保存中間結果,避免重復計算 for(int i=2;i<=n;i++) { fn=pre+post;//fn等於其前面兩個元素值的和 //然后讓pre和post分別直線他們后面的元素。 pre=post; post=fn; } return fn; } }
題目2:一只青蛙一次可以跳上1級台階,也可以跳上2級台階。求該青蛙跳上一個n級的台階總共有多少種跳法。
這道題目的本質就是斐波那契數列。假設只有一個台階,那么只有一種跳法,那就是一次跳一級,f(1)=1;如果有兩個台階,那么有兩種跳法,第一種跳法是一次跳一級,第二種跳法是一次跳兩級,f(2)=2。如果有大於2級的n級台階,那么假如第一次跳一級台階,剩下還有n-1級台階,有f(n-1)種跳法,假如第一次條2級台階,剩下n-2級台階,有f(n-2)種跳法。這就表示f(n)=f(n-1)+f(n-2)。將上面的斐波那契數列代碼稍微改一下就是本題的答案。這列略之。
題目3:我們可以用2X1(2行1列)的小矩形橫着或者豎着去覆蓋更大的矩形。請問用8個2X1的小矩形無重復地覆蓋一個2X8的大矩形,總共有多少種方法。
這道題目還是斐波那契數列的變種。設f(8)表示覆蓋2X8大矩形的方法綜述。假設第一個小矩形是豎着去覆蓋大矩形,那么還剩下由7個2X1的小矩形組成的大矩形f(7);假設第一個小矩形是橫着去覆蓋大矩形,那么還剩下由6個2X1的小矩形組成的大矩形f(6)。即f(8)=f(7)+f(6)。依此類推,最后f(1)=1,f(2)=2。使用計算斐波那契數列的方法計算這道題目即可求出答案。