認識斐波拉契數列
第n 個數由數列的前兩個相加而來: f(n) = f(n - 1) + f(n -2),用代碼實現斐波拉契數列,無非就是要考察遞歸的寫法,但是,單純使用遞歸,在嚴格要求時間複雜度和空間複雜度上是不可行的,因爲他做了無數次無用的計算.
1, 1, 2, 3, 5, 8, 13, …
普通實現 (遞歸)
這種方式計算的時候會有很多重複的計算,而且遞歸的層數越來越深容易遞歸爆棧。
const fib = n => n <= 1 ? n :fib(n-1)+fib(n-2)
console.log(fib(5))
減少時間複雜度(閉包+遞歸)
通過增加一層緩存,用來存放之前已經計算過的數值,當需要計算新值的時候先通過查找緩存,緩存命中則直接返回,未命中再繼續計算。但是使用了數組,增加了空間複雜度.
const fib3 = function (n) {
n <= 1 && n
const cache = []
cache[0] = 1
cache[1] = 1
function memoize(num) {
if (cache[num] !== undefined) {
return cache[num]
}
cache[num] = memoize(num - 1) + memoize(num - 2)
// console.log(cache[num])
return cache[num]
}
const result = memoize(n-1)
return result
}
console.log(fib3(4))
但是這樣的實現方式有一個問題,如果不是依次計算斐波那契數列就會增加額外的消耗,比如直接計算 fib(1000) ,這個時候數組中會先初始化中間的其他數組項爲 undefined 這裏會小一些時間,但是計算完畢之後1-1000之間的斐波那契數列都填充完畢了。
參考網上的一種解法是用對象替換數組來進行緩存,這樣就少去了填充 undefined 的時間
// 閉包 + 緩存對象
const fibonacci = (function () {
const f = {}
return function(n) {
if(n === 0 || n === 1) {
return n
}
if(f[n-2] === undefined) {
f[n-2] = fibonacci(n-2)
}
if(f[n-1] === undefined) {
f[n-1] = fibonacci(n-1)
}
return f[n] = f[n-1] + f[n-2]
}
})()
換一種從前往後算的寫法:::從前面的數一步一步的往後計算一直計算到n
const fib =function (n){
n <= 1 && n
const cache = []
cache[0] = 1
cache[1] = 1
for (let i= 2; i<=n; i++ ) {
cache[i] = cache[i-1] +cache[i-2]
}
return cache[n-1]
}
console.log(fib(5))
減少空間複雜度
不使用和數組,減少空間維度 從前往後算
const fib = n =>{
n <= 1 && n
let prev2 = 0
let prev1 = 1
let result = 0
for (let i = 2;i<=n;i++) {
result =prev2+prev1
prev2 = prev1
prev1 = result
}
return result
}