注: 本文使用三種方法實現Fibonacci Sequence:遞歸法、非遞歸法、矩陣快速冪法
Leetcode 70
You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
Note: Given n will be a positive integer.
題意:
- 本題描述的是一個爬樓梯問題,你需要n steps才能到達最高點,而每次你只能跨1~2個steps,問你有多少種到達最高點的走法?
思路:
假設我k次到達樓頂,那麼當我完成第k-1次時,第k次有兩種情況:1 steps or 2 steps ( 注:k與本題解題無關,定義它只是爲了方便說明最後一次有1-steps和2-steps兩種情況)。所以,假設n steps到達樓頂有F(n)種走法,那麼顯然F(n) = F(n-1)+F(n-2)。看到這個通項,立刻就反應過來,這道題考察的是斐波那契數列。
雖然通項出來了,還是要看看前幾項。不難分析,F(1) = 1, F(2) = 2, F (3) = 3。這說明只用到斐波那契的一部分,即從斐波那契數列第二項開始,而不包含第一項。這一點在寫代碼是要注意。
解題:
遞歸寫法,十分簡練,但效率極低,顯然會超時!
時間複雜度分析需要用到二階常係數齊次差分方程相關知識,看這裏
class Solution { public: int climbStairs(int n) { if (n == 0) return 1; if (n == 1) return 1; return climbStairs(n-1) + climbStairs(n-2); } };
非遞歸寫法,時間複雜度爲O(n), 本題可以通過!
class Solution { public: int climbStairs(int n) { //if (n == 0) return 1; //if (n == 1) return 1; //return climbStairs(n-1) + climbStairs(n-2); if (n == 1) return 1; int a[n+1]; a[0] = 1; a[1] = 1; for (int i = 2; i <= n; i++) { a[i] = a[i-1] + a[i-2]; } return a[n]; } };
除了以上兩種方法,斐波那契數列還可以通過矩陣快速冪法求解。
先談談快速冪(Fast Exponentiation):
快速冪就是快速算底數的n次冪,時間複雜度O(log N)
例如2^10, 常規計算需要9次乘法(2*2*2…*2),而使用快速冪,只需要2次。
10的二進制表示:1010, 對應爲2^3,2^2,2^1,2^0, 即有1的位對應2^3, 2^1
2^10 = 2^8 * 2^2 = 2^(2^3) * 2^(2^1) 指數部分剛好對應10的二進制
以上就是快速冪的核心思想,下面是我自己寫的一個快速冪:
/* 2017/9/8: Fast_Exponentiation.cpp 快速冪 原理: eg:10的二進制表示:1010, 對應爲2^3,2^2,2^1,2^0 2^10 = 2^8 * 2^2 = 2^(2^3) * 2^(2^1) 指數部分剛好對應10的二進制 */ # include <iostream> using namespace std; int main(void) { // base: 底 // e: 冪 // result: 計算結果 int base, e; int result = 1; int count = 0; cin >> base >> e; // 快速冪算法核心部分 while(e) { if (e & 1) { // 判斷最後一位是否爲1 result = result * base; } base = base * base; e = e >> 1; count++; } cout << "The result is: " << result << endl; cout << "The num of the loop is " << count << endl; return 0; }
斐波那契數列也可以快速冪的思想實現,只不過它的快速冪是用於矩陣,即矩陣快速冪算法,下面是我自己寫的一個程序:
-
/* 2017/9/8 Fibonacci.cpp 思路:利用矩陣快速冪的思路解題 */ # include <iostream> # include <cstring> using namespace std; typedef long long ll; // 定義矩陣 struct Matrix { ll m[2][2]; }; // 矩陣冪的底 Matrix base = {0, 1, 1, 1}; // 矩陣乘法 Matrix multi(const Matrix& A, const Matrix&B) { Matrix result; memset(result.m, 0, sizeof(result)); for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { for (int k = 0; k < 2; k++) { result.m[i][j] += A.m[i][k]*B.m[k][j]; } } } return result; } // 矩陣的N次方,矩陣快速冪的核心 Matrix power(int N) { Matrix result = {1, 0, 0, 1}; while(N) { if (N & 1) { result = multi(result, base); /* for (int i = 0; i < 2; i++) { cout << endl; for (int j = 0; j < 2; j++) { cout << result.m[i][j] << " "; } } */ } base = multi(base, base); N >>= 1; } return result; } // 計算,調用子函數 ll calcu(int N) { if (N == 0) return 0; int e = N-1; Matrix p = power(e); ll result = p.m[1][0]*0 + p.m[1][1]*1; return result; } // 主函數 int main(void) { int N; cin >> N; ll result; result = calcu(N); cout << "The result is " << result << endl; return 0; }
以上內容皆爲本人觀點,歡迎大家提出批評和指導,我們一起探討!