LeetCode 第70题动态规划算法
导言
看了 动态规划(https://www.cnblogs.com/fivestudy/p/11855853.html)的帖子,觉得写的很好,记录下来。
动态规划问题一直是算法面试当中的重点和难点,并且动态规划这种通过空间换取时间的算法思想在实际的工作中也会被频繁用到,这篇文章的目的主要是解释清楚 什么是动态规划,还有就是面对一道动态规划问题,一般的 思考步骤 以及其中的注意事项等等,最后通过几道题目将理论和实践结合。
用一句话解释动态规划就是 “记住你之前做过的事”,如果更准确些,其实是 “记住你之前得到的答案”。
示例
LeetCode 第70题爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
题目解析
爬楼梯,可以爬一步也可以爬两步,问有多少种不同的方式到达终点,我们按照上面提到的四个步骤进行分析:
-
问题拆解:
我们到达第 n 个楼梯可以从第 n - 1 个楼梯和第 n - 2 个楼梯到达,因此第 n 个问题可以拆解成第 n - 1 个问题和第 n - 2 个问题,第 n - 1 个问题和第 n - 2 个问题又可以继续往下拆,直到第 0 个问题,也就是第 0 个楼梯 (起点)
-
状态定义
“问题拆解” 中已经提到了,第 n 个楼梯会和第 n - 1 和第 n - 2 个楼梯有关联,那么具体的联系是什么呢?你可以这样思考,第 n - 1 个问题里面的答案其实是从起点到达第 n - 1 个楼梯的路径总数,n - 2 同理,从第 n - 1 个楼梯可以到达第 n 个楼梯,从第 n - 2 也可以,并且路径没有重复,因此我们可以把第 i 个状态定义为 “从起点到达第 i 个楼梯的路径总数”,状态之间的联系其实是相加的关系。
-
递推方程
“状态定义” 中我们已经定义好了状态,也知道第 i 个状态可以由第 i - 1 个状态和第 i - 2 个状态通过相加得到,因此递推方程就出来了
dp[i] = dp[i - 1] + dp[i - 2]
-
实现
你其实可以从递推方程看到,我们需要有一个初始值来方便我们计算,起始位置不需要移动
dp[0] = 0
,第 1 层楼梯只能从起始位置到达,因此dp[1] = 1
,第 2 层楼梯可以从起始位置和第 1 层楼梯到达,因此dp[2] = 2
,有了这些初始值,后面就可以通过这几个初始值进行递推得到。
参考代码1:
def getn(n): if n == 1: return 1 dp = [0]*(n+1) dp[1] = 1 dp[2] = 2 for i in range(3, n+1): dp[i] = dp[i-1] + dp[i-2] return dp[n]
参考代码2:
生成器版本
def climbStairs(max): n, a, b = 0, 1, 1 while n < max: yield b a, b = b, a + b n = n + 1
参考代码3:
实际上这个问题是斐波那契数列的变形,所以我们可以写出这样非常简洁的代码
def cli(n): """ :type n: int :rtype: int """ x, y = 1, 1 for _ in range(n): x, y = y, x + y return x
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?