斐波那切數列
前兩位是固定的 1, 1 第三位開始,當前位是前兩位相加的和
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
面試題解:
給出第n位,問第n位的值為多少
class Solution {
/**
* 迴圈思想
* 傳統斐波那切數列解法
* @param n 第幾位
* @returns { Number } 第幾位的值
*/
public static classicFib(n: number): number {
if (n <= 1) { return n; }
let a = 1, b = 1, c;
for (let i = 3; i <= n; i++) {
c = a + b;
a = b;
b = c;
}
return c;
}
/**
* 遞迴思想
* 利用斐波那切數列的規則,第n位的值 = 第n-1位 + 第n-2位
* @param n 第幾位
*/
public static newFib(n: number): number {
if (n <= 1) { return n; }
return this.newFib(n - 1) + this.newFib(n - 2);
}
}
複製程式碼
LeetCode演算法題解
斐波那契數,通常用 F(n) 表示,形成的序列稱為斐波那契數列。該數列由 0 和 1 開始,後面的每一項數字都是前面兩項數字的和。也就是:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
給定 N,計算 F(N)。
這道題是典型的動態規劃問題
- 什麼是動態規劃:動態規劃就是 遞迴 + 記憶化
在遞迴的時候,經常會進行很多重複的計算,導致時間複雜度是指數級的。
動態規劃時,在遞迴的時候,快取上一些可以重複利用的值
從上圖可以看出:
- 計算fb(5) 需要計算 fb(4) + fb(3)
- 計算fb(4) 需要計算 fb(3) + fb(2)
- 計算fb(3) 需要計算 fb(2) + fn(1)
- ...
中間我們重複計算了很多次fb(3) fb(2) fb(1)
計算的數越大 那麼重複計算的次數也就越多, 若果把計算過的數存起來,可以節約很多計算量
class Memoize {
// 快取已經計算的斐波那切數
private static cache: number[] = [0, 1]
/**
* 自頂向下的 動態規劃 解 斐波那切數
* @param n 第幾位數
*/
public static memoize(n: number) {
if (n < 1) return n;
// 如果已經快取就取快取
if (this.cache[n] !== undefined) return this.cache[n];
// 沒有快取就計算出來快取進去
this.cache[n] = this.memoize(n - 1) + this.memoize(n - 2);
return this.cache[n]
}
}
複製程式碼
動態規劃
經典試題:青蛙跳臺階問題
一隻青蛙一次可以跳上1級臺階,也可以跳上2級臺階。求該青蛙跳上一個n級的臺階總共有多少種跳法?
- 如果這隻青蛙跳上了第N級臺階,那麼最後一次跳躍之前,它一定在第 N-1 級 或者第 N-2級臺階上。
- 那麼跳上第N級臺階有多少種跳法就變成了兩個子問題
- 跳上第 N-1 級臺階的跳法 加上 跳上第 N-2 級的跳法
- 迴圈
f(n) = f(n -1) + f(n -2)
可以看出 青蛙跳臺階問題 就是一種斐波那契數列問題
// 青蛙跳臺階問題
function jump(n: number): number {
if (n <= 0) return -1;
if (n === 1) return 1;
if (n === 2) return 2;
return jump(n - 1) + jump(n - 2);
}
複製程式碼
青蛙跳臺階的變型版
一隻青蛙一次可以跳上1級臺階,也可以跳上2級臺階,或者N級臺階。求該青蛙跳上一個N級的臺階總共有多少種跳法?
- 可以從 n-1 跳到 n
- 可以從 n-2 跳到 n
- 可以從 n-3 跳到 n
- ...
- 也可以從 0 直接跳到 n
所以 fn = f(n -1) + f(n - 2).....+ f(1) + f(0)
// 青蛙跳臺階的變型版
function jump2(n: number): number {
if (n <= 0) return -1;
if (n === 1) return 1;
if (n === 2) return 2;
let result = 0;
for (let i = 1; i < n; i++) { // 跳的時候至少要跳一級臺階,所以從 1 開始
result += jump(n - i);
}
return result + 1; // 從0級直接跳到n級 所以+1
}
複製程式碼