携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情
没想到毕业后还能遇到斐波那契数列,不禁想起了那些年被高数支配的恐惧.....但是咱要卷算法,那就卷起来!
它应该算是动态规划的入门题目了,在力扣上找到了对应的题目509,标的是简单,那么就一起探讨下这道题,从而入门动态规划吧。
题目: 斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n ,请计算 F(n) 。
暴力递归
这个题拿到手,第一反应就是暴力递归
function fib (n) {
return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}
暴力法解出来是没什么问题哈,自我感觉还代码量极少,但是看到这打败的人数,emmmm...性能太差了,时间复杂度有O(2^N),是指数级别,如果面试时这样写,直接回家等通知吧。那我们试试下一种方案。
递归+记忆化
递归是自顶向下,实质就是存储每次计算过的值,减少重复计算
function fib (n) {
const map = {}
const sub = function (x) {
if (map[x]) return map[x]
if (x === 0) return 0
if (x === 1) return 1
map[x] = sub(x-1) + sub(x-2)
return map[x]
}
return sub(n)
}
这种方案由于存储了计算过的值,减少了重复计算,所以复杂度会好一些,但是存储增加了空间复杂度
动态规划
动态规划就不用递归了,用循环自底向上,可省略记忆化过程
function fib (n) {
let dp = [0, 1]
for (let i = 2; i <= n; i++) {
dp[i] = dp[i-1] + dp[i-2]
}
return dp[n]
}
当然,这种方案空间复杂度O(N)仍然存在,时间复杂度O(N)
动态规划的状态压缩
优化上个方案中空间复杂度
function fib (n) {
if (n <= 1) return n
let p = 0
let q = 1
let r = 1
for (let i = 2; i <= n; i++) {
r = p + q
p = q
q = r
}
return r
}
用有限的变量存储当前数及上两个数,空间复杂度变为O(1),但这种方案无法记住中间状态的值,对有些场景不适用