爬楼梯 递归与非递归,动态规划
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
思路:
所有学算法和数据结构,甚至是小学生数学竞赛都必然遇见的题目了。经典的斐波那契数列。结论:爬到第n个台阶的方法,等于爬到第n-1个台阶的方法数+爬到第n-2个台阶的方法数。原理在于:我们要爬到第n个台阶,就必然要从第n-1或者n-2个台阶处迈上去。
最方便的方法就是递归,定义函数f(n),返回f(n-1)+f(n-2),再定义好base case,如f(0),f(1),f(2)的值就好了。
但递归看似很方便简单,但它的复杂度其实很高:因为他涉及到大量的重复计算。比如我们算f(5)的时候,会去算f(3),算f(4)的时候又会算f(3),可以画一棵树来:f(5)两个分支:f(4)和f(3),f(4)又开出两个分支:f(3)和f(2)……这两个出现的f(3)会分别计算,可想而知,假如数据量很大的话,这个方法是非常低效的,每一次都在不断地向下开支散叶,复杂度达2的n次方。
对递归的优化办法是:加入记忆字典、记忆数组。这个很好理解,我们定义一个全局的数组nums或者字典dict,每次计算除了一个新的f(n)值,就存储在nums[i],或者dic[i]位置上。下次再遇到计算f(n)时,就先在对应记忆数组或字典里寻找,有的话就不再进行计算了。通过牺牲了O(n)的空间复杂度,我们成功地将递归剪枝,将时间复杂度由O(2的n次)降为O(n)。
还有第三种方法,前两种递归的方法,其实都是自顶向下的,所以会造成重复计算的风险。我们其实可以直接自底向上去逼近目标,就可以按部就班的计算出最终结果。定义一个存放结果的数组,从底向上地去生成即可。发现了吗——这其实也有点像动态规划的意思了。
代码(方法3):
class Solution(object):
def climbStairs(self, n):
dp=[0]*(n+2)#dp[n]表示到第n阶的方法数
dp[0]=1#初始化
dp[1]=1
for i in range(2,n+1):#自底向上去生成 直到最终结果
dp[i] = dp[i-1]+dp[i-2]
return dp[n]
小结:
代码中初始化dp时有个(n+2),这是因为下标从0开始,如果不+2会越界。其实+3,+4都一样,最终我们能取到dp[n]即可。
这道题虽然很简单,但非常值得拿来理解递归以及动态规划的思想。前两种方法可以自己练习写出。但从复杂度和方便实现的角度上,最推荐第三种也就是我给出代码的这种来实现。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了