面試題9:斐波那契數列

來源:互聯網
上載者:User
文章目錄
  • 方法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;    }}
注意點:
  1. 因為不知道要求的f(n)中的n有多大,因此不能事先開闢一個數組,需要動態建立數組。而動態數組與陣列變數不同,動態分配的數組將一直存在,直到程式顯式釋放它為止。普通的陣列變數,只要出了數組的作用於,其記憶體會自動釋放。
  2. 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。使用計算斐波那契數列的方法計算這道題目即可求出答案。

 

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.