登台阶爬楼梯问题
问题描述
有N个台阶,0<N<=50, 每次只跳1个或者3个台阶,输入N, 有多少种方式到达台阶终点
# 示例 1:
输入:n = 3
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶+1阶
2. 3阶
# 示例 2:
输入:n = 4
输出:3
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1阶 + 1阶
2. 3阶 + 1阶
3. 1阶 + 3阶
很多时候我们自己对问题的描述所呈现的大都基于固有的思想,我们需要在固有的思想上怎么去优化这个问题才是重点。
-- 首先我们需要寻找规律
台阶1 【1】
台阶2 【11】
台阶3 【111,3】
台阶4 【13 ,31 ,1111】
台阶5 【113 ,311 ,131,111】
台阶6 【1113,1131,1311,3111,111111,33】
台阶7 【11113 ,11131,11311,13111,31111,331,133,313,1111111】
f(1) = 1
f(2) = 1
f(3) = 2
f(4) = f(1) + f(3) = 3
f(5) = f(2) + f(4) = 4
f(6) = f(3) + f(5) = 6
f(7) = f(4) + f(6) = 9
...
F(N) = F(N-3) + F(N-1)
其实很好理解:等台阶 只有两种:1 和 3 需要统计的是这两种的个数 也就是我们需要爬的总数。
第 n 级台阶的方案数是爬到第 n−1 级台阶的方案数和爬到第 n−3 级台阶的方案数的和.
递归
#普通递归 重复计算之前的树节点 时间复杂度 O(2^n) 空间复杂度(On)
#优化方法 在此基础上保存之前计算过的节点 使得时间复杂度(On)
def f1(n):
if n==1:return 1
elif n==2:return 1
elif n==3:return 2
else:
res=f1(n-1)+f1(n-3)
return res
[f1(i) for i in range(1,10)]
#[1, 1, 2, 3, 4, 6, 9, 13, 19]
#优化 存储之间计算的节点数据 如计算f(5)时 不必再次计算f(4)和f(2) 存储
def f11(n):
memo=[]
if n == 0 or n == 1:
return 1
if memo[n] == -1:
memo[n] = f11(n- 1, memo) + f11(n - 3, memo)
return memo[n]
# memo: [-1] * (n - 1)
# -1 表示没有计算过,最大索引为 n,因此数组大小需要 n + 1
return f11(n, [-1] * (n + 1))
动态规划
# 动态规划
def funa(n):
if n<2:
return 1
if n>=2:
dp = [0]*(n+1)
dp[0] = 1
dp[1] = 1
dp[2] = 2
for i in range(3,n+1):
dp[i] = dp[i-1] + dp[i-3]
return dp[n-1]
#空间复杂度O(n) 时间复杂度O(n)
[funa(i) for i in range(1,10)]
#[1, 1, 2, 3, 4, 6, 9, 13, 19]
滚动数组
#动态规划
def f3(n):
if n==0:return 0#初始化前3项
if n==1:return 1
if n==2:return 1
if n==3:return 2
p,q,r=1,1,2
# 从第4项开始遍历,由于n算在结果中,故range第二个数为n+1,区间为[3, n+1)
for i in range(4,n+1):#节省空间 不使用数组
p ,q, r = q , r ,(r+p)#每一项都是 前i-1 和 前 i-3 通过移动状态
return r
# 时间复杂度:循环执行 nn 次,每次花费常数的时间代价,故渐进时间复杂度为 O(n)。
# 空间复杂度:这里只用了常数个变量作为辅助空间,故渐进空间复杂度为 O(1)。
[f3(i) for i in range(1,10)]
#[1, 1, 2, 3, 4, 6, 9, 13, 19]