本文参考文章:
《漫画:什么是动态规划?(整合版)》 https://mp.weixin.qq.com/s/3h9iqU4rdH3EIy5m6AzXsg
题1: 爬楼梯: https://leetcode-cn.com/problems/climbing-stairs/
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
- 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
原文章主要借由 2 个问题 来讲述什么是动态规划,由浅到深,本篇主要围绕第1个问题,使用python 实现(文中使用java实现)。
第2个问题放在下一篇实现。
# 针对试题一 的解法
# 最优解 斐波拉契数列
class Solution(object):
def climbStairs(self, n):
"""
:type n: int
:rtype: int
时间复杂度: O(2^n) ; 这是一颗二叉树,树的高度 n-1 ,节点有 2^(n-1)
空间复杂度: O(2^n) ; 因为每个计算结果都要存储,而且涉及重复计算
"""
assert isinstance(n,int),"n 不是整型,请确认后重新输入"
assert (n > 0), "n 必须大于0,请确认后重新输入"
if n <= 1:
return 1
elif n == 2:
return 2
return self.climbStairs(n-1)+self.climbStairs(n-2)
def climbStairs2(self, n, record=dict()):
"""
:type n: int
:rtype: int
备忘录算法,通过一个缓存,来减少重复计算
时间复杂度: O(n) ; f1~fn 除去 1,2 确定,其他都有走一遍,即 n-2
空间复杂度: O(n) ; 同理
"""
assert isinstance(n,int),"n 不是整型,请确认后重新输入"
assert (n > 0), "n 必须大于0,请确认后重新输入"
if n <= 1:
return 1
if n == 2:
return 2
if record.keys().__contains__(n):
return record.get(n)
else:
res = self.climbStairs2(n-1,record)+self.climbStairs2(n-2,record)
record[n]= res
return res
def climbStairs3(self, n):
"""
:type n: int
:rtype: int
减少空间复杂度,自底向上
时间复杂度: O(n) ; f1~fn 除去 1,2 确定,其他都有走一遍,即 n-2
空间复杂度: O(1) ; 因为每个计算结果都要存储两个变量
"""
assert isinstance(n,int),"n 不是整型,请确认后重新输入"
assert (n > 0), "n 必须大于0,请确认后重新输入"
if n == 1:
return 1
elif n == 2:
return 2
a,b,temp = (1,2,0)
for i in range(3,n+1):
temp = a+b
a = b
b = temp
return temp
if __name__ == '__main__':
s = Solution()
# res = s.climbStairs(4)
# res = s.climbStairs2(4)
res = s.climbStairs3(4)
print(res)
思路: climbStairs函数 是原始解法,但时间及空间复杂度均很大 O(2^n), 考虑到有重复计算,
所以加个缓存备忘录 字典record, 没有的加进去,后续再遇到就直接从record中取值,没必要再递归到最底层得到结果。即 climbStairs2 函数
再进一步考虑优化 空间复杂度,自底向上,每次只保留最近的两个结果,即 climbStairs3 函数