高频算法面试题(二十六)- 斐波那契数列(动态规划)

195 阅读3分钟

「这是我参与11月更文挑战的第 13 天,活动详情查看:2021最后一次更文挑战

刷算法题,从来不是为了记题,而是练习把实际的问题抽象成具体的数据结构或算法模型,然后利用对应的数据结构或算法模型来进行解题。个人觉得,带着这种思维刷题,不仅能解决面试问题,也能更多的学会在日常工作中思考,如何将实际的场景抽象成相应的算法模型,从而提高代码的质量和性能

斐波那契数列

题目来源LeetCode-509. 斐波那契数

题目描述

斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0F(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)

4.png 可以发现这里边颜色相同的,都是有重复计算的,导致递归实现的时候,它的时间复杂度是指数级别的。用动态规划来解,其实就像我上边通过一个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]
}