文章目錄
- 方法1:使用遞迴解,時間複雜度是n的指數層級
- 方法2:開闢一個長度為(n+1)的數組,時間複雜度為O(n),空間複雜度為O(n)
- 注意點:
- 方法3:最佳化方法2,空間複雜度為O(1),時間複雜度為O(n)
題目1:寫一個函數,輸入n,其斐波那契數列的第n項。
斐波那契數列的定義如下:
方法1:使用遞迴解,時間複雜度是n的指數層級
斐波那契數列的定義就是遞迴的,我們根據定義可以很簡單的寫出代碼。代碼如下:
View Code
#include<iostream>#include<stdlib.h>using namespace std;//f(n)={0,1,1,2,3...} n>=0int 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]上,這樣計算斐波那契數列就相當於是一個填表的過程。時間複雜度大大降低。代碼執行個體如下:
View Code
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中,我們儲存了每一個中間變數,但是仔細觀察我們可以發現沒有必要儲存每一個中間變數,我們只需要儲存兩個臨時變數即可完成斐波那契數列的計算。具體代碼試下如下:
View Code
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。使用計算斐波那契數列的方法計算這道題目即可求出答案。