1.问题分析
-- 如果想要到达第n阶台阶
-- 倒数一步有两种走法:从第n-1台阶跳上来或从第n-2台阶跳上来
-- 那么到n-1阶台阶的走法有f(n-1)个,到n-2阶台阶的走法有f(n-2)个
-- 两种走法的种数相加就是n阶台阶的情况下的所有种数
-- 即f(n)=f(n-1)+f(n-2)
2.代码(递归解法)
def f(n):
if n==1: # 递归结束条件
return 1
elif n==2: # 递归结束条件
return 2
else:
return f(n-1)+f(n-2)
print(f(20))
# 10946
时间复杂度:因为在递归过程中,会有大量的重复计算。例如,计算 f(n)
时会计算 f(n - 1)
和 f(n - 2)
,而计算 f(n - 1)
时又会计算 f(n - 2)
和 f(n - 3)
,其中 f(n - 2)
就被重复计算了。随着 n
的增大,重复计算的数量呈指数级增长。
3.代码优化
# 以爬5阶台阶为例
def f(n,m={}):
# 建立一个空字典m
if n<=2:
return n
if n not in m:
m[n]=f(n-1,m)+f(n-2,m)
# 如果数值不在字典中,将递归后的值保存到字典中
# 作用:下次直接调用结果,不用重复计算
return m[n]
print(f(5))
代码执行过程
调用 f(5) 时,5 不在字典 m 中,所以需要计算 f(4, m) + f(3, m)。
计算 f(4, m) 时,4 也不在字典 m 中,需要计算 f(3, m) + f(2, m)。
计算 f(3, m) 时,3 不在字典 m 中,需要计算 f(2, m) + f(1, m)。
计算 f(2, m),由于 n <= 2,直接返回 2。
计算 f(1, m),由于 n <= 2,直接返回 1。
所以 f(3, m) 为 2 + 1 = 3,并将 m[3] = 3 存入字典。
此时 f(4, m) 为 3 + 2 = 5,并将 m[4] = 5 存入字典。
计算 f(3, m) 时,由于之前已经计算过并存入字典,直接从字典中获取值 3。
最终 f(5) 为 5 + 3 = 8,并将 m[5] = 8 存入字典,最后打印输出 8。
def f(n,m={}):
if n<=2:
return n
if n not in m:
m[n]=f(n-1,m)+f(n-2,m)
print(m)
return m[n]
print(f(5))
# 输出
# {3: 3}
# {3: 3, 4: 5}
# {3: 3, 4: 5, 5: 8}
# 8
4.动态规划解法
随着台阶数的增加,走法数也不断增加,数值增长情况也斐波那契数列相同
斐波那契数列,又称为兔子数列,1,1,2,3,5,8,13,21.....下一个数等于前两个数之和
5.代码
# 动态规划
def f(n):
if n<=2:
return n
a=1
b=2
sum=0
for i in range(2,n):
sum=a+b
a=b
b=sum
return sum
print(f(5))
#第一次循环:sum=a+b=3
# a=b=2
# b=sum=3
#第二次循环:sum=a+b=5
# a=b=3
# b=sum=5
#第三次循环:sum=a+b=8
#即a和b依次代表前两个值,sum为前两个值之和
6.拓展:斐波那契数列
# 斐波那契数列,又称为兔子数列,1,1,2,3,5,8,13,21.....
# 下一个数等于前两个数之和
n=20
list=[1,1]
for i in range(2,n+1):
a=list[i-2]
b=list[i-1]
list.append(a+b)
print(list)
# [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,
# 377, 610, 987, 1597, 2584, 4181, 6765, 10946]
# 列表中下标为20的值,即为爬20个台阶的方法