天天看點

【算法入門07】斐波那契數列

核心考點:空間複雜度,fib了解,剪枝重複計算

大家都知道斐波那契數列,現在要求輸入一個正整數n,請你輸出斐波那契數列的第n項。

斐波那契數列是一個滿足

的數列。

​​​​​​​​​​​​​​​​​

解析一:(非常不提倡)

題目已經給出了斐波那契數列的狀态轉移方程,是以我們可以很容易使用遞歸的方式來求得第n個斐波那契數。

但如果使用遞歸來解決該問題,就不得不面對一個問題,那就是大量的重複計算,若是我們要得到第40個斐波那契數,那我們大緻需要經曆以下路徑。

【算法入門07】斐波那契數列

可以看到,圖中對于同一斐波那契數進行了多次計算,而且越往下對于同一斐波那契數的重複計算次數越多,是以并不提倡使用遞歸的方法解決該問題。

//遞歸
class Solution {
public:
  int Fibonacci(int n) {
    if (n == 1 || n == 2) //fib(1)=1, fib(2)=1
      return 1;
    return Fibonacci(n - 1) + Fibonacci(n - 2); //fib(n)=fib(n-1)+fib(n-2)
  }
};      

解析二:(不提倡)

既然使用遞歸存在大量的重複計算,那麼我們可以考慮将已經計算過的斐波那契數儲存起來,這樣便避免了斐波那契數的重複計算。

例如,在計算第40個斐波那契數的中途需要計算第38個斐波那契數,而第38個斐波那契數已經被計算過了,那就無需再從此處遞歸下去了。

【算法入門07】斐波那契數列

從圖中看來就是不用再計算二叉樹的某一枝葉了,是以該方法被形象的稱為“剪枝”。

将遞歸和剪枝配合起來使用,其時間複雜度相比單純的遞歸來說簡直快了太多了,但該方法也有一個弊端,那就是需要額外的記憶體空間來暫時儲存已經計算過的斐波那契數。

//遞歸+剪枝
class Solution {
private:
  unordered_map<int, int> filter; //存儲已經計算過的斐波那契數
public:
  int Fibonacci(int n) {
    if (n == 1 || n == 2) //fib(1)=1, fib(2)=1
      return 1;

    int pper = 0; //第n-2個斐波那契數
    if (filter.find(n - 2) == filter.end())
    {
      //在map當中沒有找到第n-2個斐波那契數,需要計算
      pper = Fibonacci(n - 2);
      filter.insert({ n - 2, pper });
    }
    else
    {
      //在map當中找到了第n-2個斐波那契數,直接擷取
      pper = filter[n - 2];
    }

    int per = 0; //第n-1個斐波那契數
    if (filter.find(n - 1) == filter.end())
    {
      //在map當中沒有找到第n-1個斐波那契數,需要計算
      per = Fibonacci(n - 1);
      filter.insert({ n - 1, per });
    }
    else
    {
      //在map當中找到了第n-1個斐波那契數,直接擷取
      per = filter[n - 1];
    }

    return pper + per; //fib(n)=fib(n-1)+fib(n-2)
  }
};      

解析三:(正解)

遞歸實際上是從終點看向起點,現在我們從起點看向終點,從第一個斐波那契數往後進行計算,需要第幾個斐波那契數就計算到第幾個。

而在該過程中我們不需要将所有計算過的斐波那契數全部儲存起來,因為一個斐波那契數的值隻與其前面兩個斐波那契數有關。

【算法入門07】斐波那契數列
//動規
class Solution {
public:
  int Fibonacci(int n) {
    if (n == 1 || n == 2) //fib(1)=1, fib(2)=1
      return 1;
    int first = 1; //fib(1)=1
    int second = 1; //fib(2)=1
    int third = 0;
    while (n > 2) //進行n-2次計算
    {
      //fib(n)=fib(n-1)+fib(n-2)
      third = first + second;
      first = second;
      second = third;
      n--;
    }
    return third;
  }
};      

繼續閱讀