递归的本质
递归函数的本质是“(函数)栈”的使用,所以我们如果可以手动模拟这个栈及出栈入栈的过程,那就可以免掉函数多次调用的开销,将递归转换为非递归
一个求全排列的例子
递归方式
class Permute:
@staticmethod
def get_all_permute(l_raw: list) -> list:
list_all = []
def recursive_permute(l_tmp: list, l_res: list = []) -> None:
if len(l_tmp) == 1:
l_res.append(l_tmp[0])
list_all.append(l_res)
return
for i in range(len(l_tmp)):
recursive_permute(l_tmp[:i] + l_tmp[i + 1:], l_res + [l_tmp[i]])
recursive_permute(l_raw)
return list_all
if __name__ == "__main__":
list_a = [1, 2, 3, 4]
print(Permute.get_all_permute(list_a))
[[1, 2, 3, 4], [1, 2, 4, 3], [1, 3, 2, 4], [1, 3, 4, 2],
[1, 4, 2, 3], [1, 4, 3, 2], [2, 1, 3, 4], [2, 1, 4, 3],
[2, 3, 1, 4], [2, 3, 4, 1], [2, 4, 1, 3], [2, 4, 3, 1],
[3, 1, 2, 4], [3, 1, 4, 2], [3, 2, 1, 4], [3, 2, 4, 1],
[3, 4, 1, 2], [3, 4, 2, 1], [4, 1, 2, 3], [4, 1, 3, 2],
[4, 2, 1, 3], [4, 2, 3, 1], [4, 3, 1, 2], [4, 3, 2, 1]]
改为非递归
class Permute:
@staticmethod
def get_all_permute(l_raw: list) -> list:
list_all = []
l_tmp = []
stack = list()
stack.append(l_raw)
stack.append(l_tmp)
while stack:
# 注意先进后出
l_res = stack.pop()
l_tmp = stack.pop()
if len(l_tmp) == 1:
l_res.append(l_tmp[0])
list_all.append(l_res)
for i in range(len(l_tmp)):
stack.append(l_tmp[:i] + l_tmp[i + 1:])
stack.append(l_res + [l_tmp[i]])
return list_all
if __name__ == "__main__":
list_a = [1, 2, 3, 4]
print(Permute.get_all_permute(list_a))
非递归优点:
减少函数多次调用带来的开销
不受递归深度限制影响
缺点:
难以阅读
措施:
可以先写递归,然后再转非递归
拓展1:改为迭代器 (yield from)
class Permute:
@staticmethod
def get_all_permute(l_tmp: list, l_res: list = []) -> None:
if len(l_tmp) == 1:
l_res.append(l_tmp[0])
yield l_res
for i in range(len(l_tmp)):
yield from Permute.get_all_permute(l_tmp[:i] + l_tmp[i + 1:], l_res + [l_tmp[i]])
if __name__ == "__main__":
list_a = [1, 2, 3, 4]
for item in Permute.get_all_permute(list_a):
print(item)
拓展2:尾递归
尾递归可以由编译器层面进行优化,减少函数多次调用的开销