「这是我参与11月更文挑战的第 13 天,活动详情查看:2021最后一次更文挑战」
刷算法题,从来不是为了记题,而是练习把实际的问题抽象成具体的数据结构或算法模型,然后利用对应的数据结构或算法模型来进行解题。个人觉得,带着这种思维刷题,不仅能解决面试问题,也能更多的学会在日常工作中思考,如何将实际的场景抽象成相应的算法模型,从而提高代码的质量和性能
斐波那契数列
题目来源:LeetCode-509. 斐波那契数
题目描述
斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给你 n ,请计算 F(n)
示例
示例 1
输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1
示例 2
输入:3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2
示例 3
输入:4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3
提示:
0 <= n <= 30
解题
解法一:递归
思路
这个大家肯定都非常熟悉,第一个想到的就是递归来解。关于什么样的问题适合用递归来解?适合用递归解的题有哪些特点?如何写递归的代码,可以看我的这篇文章
这道题就很简单了,求第n个斐波那契数字,只要知道前两个就可以了,所以很容易想到递归公式。我这里直接上代码。主要分享的是下边的动态规划解法
代码
//斐波那契数列
var repeatNum = map[int]int{}
func Fib(n int) int {
if n ==0 {
return 0
}
if n == 1 {
return 1
}
num, ok := repeatNum[n]
if ok {
return num
}
res := Fib(n-1) + Fib(n-2)
repeatNum[n] = res
return res
}
解法二:动态规划
思路
我们知道递归的方法来实现斐波那契数列,时间复杂度是指数级别的,之所以是指数级别的,是因为它里边存在大量的重复计算,你可以看一下我下边画的这个递归树,假设计算Fib(6)
可以发现这里边颜色相同的,都是有重复计算的,导致递归实现的时候,它的时间复杂度是指数级别的。用动态规划来解,其实就像我上边通过一个map来记录已经计算过的值,这样就不用重复计算了。动态规划不同的是,它的思想其实就是通过消除重复的状态来减少计算次数,可能这个题显式的还不是很明显,后边刷到动态规划的题,会详细介绍。下边可以看一下用动态规划来实现斐波那契数列的代码
代码
func fib(n int) int {
if (n == 0) {
return 0
}
if (n == 1) {
return 1
}
status := make([]int, n+1)
status[0] = 0
status[1] = 1
for j:=2; j <= n; j++ {
status[j] = status[j-1] + status[j-2]
}
return status[n]
}