动态规划--爬楼梯

Posted 算法艺术

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划--爬楼梯相关的知识,希望对你有一定的参考价值。




摘要


爬楼梯是非常经典的面试问题,很多大公司都会将这个问题作为笔试的一部分,希望大家能深入理解,这个问题是LeetCode-70题,大家可以在LeetCode上找到更多相关问题,大家加油!

来源:力扣(LeetCode) 
链接:https://leetcode-cn.com/problems/climbing-stairs
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


题目描述

假设你正在爬楼梯。需要 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个台阶 




思路分析

因为每次可以走1个台阶或走2个台阶,要到达楼梯第n阶时,可以从第n-1阶向上再走1个到达n,也可以从第n-2阶再走2个台阶到达n


状态转移方程为:f(n) = f(n-1) + f(n-2)



代码实现


1

记忆化递归


算法:

自顶向下 ,此递归算法在调用的过程会有很多重复计算的数据,导致计算的次数大大增加,从而消耗时间会很长。所以,可以把每一步f(n)计算的结果存储在 memo 列表 之中,每当函数再次被调用时,如果memo[n]有值,就直接从 memo 列表返回结果。这就实现了一个 剪枝 的过程,减少了递归调用次数。
class Solution: meno = [] def climbStairs(self, n: int) -> int: if n < 0: return 0 self.meno = [-1 for i in range(n+1)] return self.getStep(n) def getStep(self,n:int) -> int:        # 状态转移方程 f(n) = f(n-1) + f(n-2)  # 自顶向下 递归 # 程序结束条件 if n == 0 or n == 1: return 1 if self.meno[n] != -1: return self.meno[n] res = self.getStep(n-1) + self.getStep(n-2) self.meno[n] = res return res
复杂度分析:

时间复杂度: O(n) ,树形递归的大小可以达到 n

空间复杂度: O(n) ,使用了空间大小为n的memo列表记录结果


2

动态规划

算法:

自低向上,可以被分解为一个最优子问题

动态转移方程:dp[n]=dp[n−1]+dp[n−2]

class Solution:  def climbStairs(self, n: int) -> int: if n < 0:            return 0 # 自低向上 动态规划 memo = [-1 for i in range(n+1)] memo[0] = 1 memo[1] = 1 for i in range(2,n+1): memo[i] = memo[i-1] + memo[i-2] return memo[n]

复杂度分析:

时间复杂度: O(n) ,只用了一个for循环,从 2 到 n+1。

空间复杂度: O(n) ,使用了空间大小为n的memo列表记录结果


3

斐波那契数


算法:

上面我们用了n个空间来保存计算出来的结果,但是我们只需要爬到第n阶楼梯共多少种方法,只保存一个数字即可,因此我们只需开辟常数级的空间保存几个变量。

class Solution:  def climbStairs(self, n: int) -> int: if n < 0: return 0        # 自低向上 动态规划        first = 1        second = 1        thrid = -1 for i in range(2,n+1):            thrid = first + second            first = second            second = thrid         return second

复杂度分析:

时间复杂度: O(n) ,只用了一个for循环,从 2 到 n+1。

空间复杂度: O(1) ,使用了常数空间,保存3个临时变量。



4

斐波那契公式

算法:

我们可以根据数学归纳法,对状态转移方程进行推导,得出数学公式。

,所以f(n+1) = a^(n+1),f(n+2) = a^(n+2),转化f(n+1) = a * a^n,f(n+2) = a^2 * a^n。

由状态方程得:

        a^2 * a^na * a^n + a^n

两边都除以a^n,并移项得:

        a^2 - a - 1 = 0    

解方程得:

        a1 = 1/sqrt(5) * ((1+sqrt(5)) / 2)

        a2 = 1/sqrt(5) * ((1-sqrt(5)) / 2)

所以一般形式为:

f(n) = 

    1/sqrt(5)*{ [ (1+sqrt(5)/ 2 ]^(n+1)- (1-sqrt(5)/ 2 ]^(n+1) }

class Solution:  def climbStairs(self, n: int) -> int: if n < 0: return 0        sqrt5 = sqrt(5)        fibn = pow((1+sqrt5)/2,n+1) - pow((1-sqrt5)/2,n+1)        res = fibn / sqrt5 return res

复杂度分析:

时间复杂度: pow 方法将会用去 的时间。

空间复杂度: O(1) ,使用了常数空间。



The End

最后给大家留两个思考问题:
        1.如果 每次可以爬  1  , 2  或者 3 个台阶,应该怎么实现
        2 .如果 每次可以爬  1  , 2...  或者 k 个台阶,k < n,应该怎么实现

一个问题的解决方法有很多,可以在时间和空间上进行优化,此问题还有更多解法,欢迎私信我,一起讨论,大家加油!



以上是关于动态规划--爬楼梯的主要内容,如果未能解决你的问题,请参考以下文章

746. 使用最小花费爬楼梯『简单』

leetcode 746. 使用最小花费爬楼梯----动态规划篇

计蒜客--爬楼梯 (动态规划)

动态规划(斐波那契系列)---爬楼梯

简单动态规划—爬楼梯

动态规划-爬楼梯问题