什么是斐波那契数列
诸如1,1,2,3,5,8,13....这样的序列,除了前两个固定是1(原因见<不死神兔>典故)从第三个开始每个数等于前两个数的和
也就是n = (n-1)+(n -2) (n>=3)
递归实现
第一个: 1
第二个: 1
第三个: 2
第四个: 3
......
按照这个规律 因为前两个始终都是1 所以我们确定了递归的出口就是 n===1和2时结果为1 每次递归就是求前两项的和
function feibonaqi(n) {
if (n === 1 || n === 2) {
return 1
}
return feibonaqi(n - 1) + feibonaqi(n - 2)
}
当n超过45时,浏览器就没有反应了,转啊转,赶紧关了...
因为第n个等于n-1+n-2的和,n-1又等于n-2+n-3的和,以此类推,存在大量的重复计算,此时我们要优化递归了,将结果用哈希表以n为key缓存起来所有的n-1+n-2
优化递归实现加缓存
const cache = new Map()
function feibonaqiMemo(n) {
if (n === 1 || n === 2) {
return 1
}
// 如果缓存有优先从缓存拿 没有就将结果存入缓存
return (
cache.get(n) ||
cache.set(n, feibonaqiMemo(n - 1) + feibonaqiMemo(n - 2)).get(n)
)
}
console.log(feibonaqiMemo(1000))
经过缓存后求1000的斐波那契数速度都非常快了,类似这样缓存的思想我们可以服用成一个缓存函数,返回一个具备缓存结果能力的缓存函数
实现缓存函数
这里可以采用闭包来存储缓存的结果
function memo(fn) {
const cache = new Map() // 缓存结果
return function (...args) {
const key = JSON.stringify(args) // 将入参做为key
// 优先从缓存取,没有则存入缓存
return cache.get(key) || cache.set(key, fn.apply(null, args)).get(key)
}
}
const feibonaqiMemo = memo(function (n) {
if (n === 1 || n === 2) {
return 1
}
return feibonaqiMemo(n - 1) + feibonaqiMemo(n - 2)
})
console.log(feibonaqiMemo(1000))
当n比较大时,比如n=4000,递归会出现栈溢出问题,递归基本上都是可以用循环替代的,我们可以用循环替代递归解决栈溢出的问题
循环实现
function feibonaqiViaLoop(n) {
const arr = [] // 用数组来存储每个n的斐波那契数
arr[0] = arr[1] = 1 // 前两个都是1直接存储
// 从第三个开始 n = n-1+n-2
let i = 2
while (i < n) {
arr[i] = arr[i - 1] + arr[i - 2]
i++
}
return arr[n - 1] // 返回第n个也就是最后一个数
}
console.time('feibonaqiViaLoop')
console.log('n=9999999:', feibonaqiViaLoop(9999999))
console.timeEnd('feibonaqiViaLoop')
这样n再大也能运行了,但是这个结果太大了所以infinity了,所以能用循环解决时尽量避免用递归