爬楼梯 递归与非递归,动态规划

70. 爬楼梯

假设你正在爬楼梯。需要 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]即可。

       这道题虽然很简单,但非常值得拿来理解递归以及动态规划的思想。前两种方法可以自己练习写出。但从复杂度和方便实现的角度上,最推荐第三种也就是我给出代码的这种来实现。

posted @   JunanP  阅读(43)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示