最近惹女朋友生气了,写一篇技术文章来安抚她~
说起递归,我们最开始想到的就是斐波那契数列,这道题十分的经典,代码如下:
def f(n):
if n <= 1:
return n
return f(n-1) + f(n-2)
递归结构最常用在树结构中,例如先序遍历等等。
其次用在回溯法中,递归可以撤销之前的操作。
同时递归还是动态规划的一种超集,其名为记忆化递归。
如果你刷题的过程中经常会头疼动态规划类型的题,那么这篇文章正适合你!
因为记忆化递归在很多场景下可以替换动态规划,其coding的难度要小于动态规划。
那么为什么要记忆化递归?什么是记忆化递归呢?
为什么记忆化递归?
回答:因为普通的递归可能会重复求解某一值,类似斐波那契数列。同样的子问题可能会被求解多次,这样就会很慢很慢很慢。
解决方法:我们把历史求解(子问题)记录下来,如果下次需要求解子问题,那么直接取出就好。其时间复杂度为O(1)。
下面的代码就是记忆化递归来求解斐波那契数列~
m = [0] * max_size # 开辟一个数组来存储,或者使用字典
def f(n):
if n <= 1:
return n
if not m[n]:
m[n] = f(n - 1) + f(n -2)
return m[n]
记忆化递归的问题求解模板
记忆化递归相比动态规划的优势在哪儿?
动态规划是有先后顺序的,子问题求解后才能求解父问题。因此你要小心确保问题求解的顺序,从而避免bug。
但是记忆化递归,当你的子问题没有求解时,程序会自动去求解。而不需要小心翼翼的去确保顺序,减轻我们的工作量。
记忆化递归相比动态规划的劣势在哪儿?
因为动态规划时有先后顺序的,因此实际上有一些求解的子问题用不到了可以将空间释放掉。
而记忆化递归再这种优化上就很难。
故记忆化递归可能在空间复杂度上比不上精心设计的动态规划。
但是!!!这里是精心设计的动态规划,为了精心设计,我们可能会花很长的时间去写控制规则,为了减少那么一丢丢的内存。但是实际上我们刷题,失败的原因往往是超时!!!而不是超出内存限制。
因此记忆化递归用于刷题还是挺舒服的~
2019/07/15
记忆化递归的一个缺点是,有可能会超越python的最大递归深度。
x = int(input())
m = [0] * (x + 1)
MAXVALUE = 100000000
def func(x):
if x < 3 :
return MAXVALUE
elif x == 3 or x == 5 or x == 7:
return 1
if not m[x]:
t = min(func(x - 3), func(x - 5), func(x - 7))
if t == MAXVALUE:
m[x] = t
else:
m[x] = t + 1
return m[x]
t = func(x)
if t == MAXVALUE:
print(-1)
else:
print(t)