递归转非递归-用栈完全模拟
本文参照此处实现: 递归函数转非递归(通用方法)[1/2]——数据结构 递归函数转非递归(通用方法)[2/2]
先说总结,这种方案总的来说就是机械化的强转,时间复杂度和空间复杂度没什么变化,唯二的优点可能是1. 不会爆栈,2. 节省了函数调用的开销
而且最终产出的代码效果不那么美观,比较冗长
思路是:当发生递归调用时,模拟函数调用的压栈。并处理入参和返回值和记录返回到当前栈的时候该继续从哪里执行
以如下递归(leetcode爬楼梯)为例
1 | def f(n): |
第一步:
将涉及到递归调用的,单独变成最简单的一行
1 | def f(n): |
第二步:
我们需要模拟递归栈调用,当执行完递归回到当前栈的时候需要知道从哪里继续执行,所以需要一个flag标记,开始的时候为0,我们先手工标记一下,再后序转换的时候可以方便查看
1 | def f(n): |
第三步:
构建解题模版
1 | def f_iter(n): |
first_frame = [(n, 'a'), {}, 0]
注意此时接收函数返回的时候为什么是一个字典,并且调用参数的时候传参多了一个’a’,因为函数被递归调用了两次,分别得到一个a和b, 所以在返回的时候需要知道返回是给a还是给b, 如果只递归调用了一次,那么就不需要带上’a’,返回的时候也不用是字典了,最后整个函数执行完成之后,base_frame里面就是最终的答案
第四步:
填充骨架,记住两点就可以了
- 函数调用的时候,先将当前栈的flag修改(等再次执行到当前栈的时候知道从哪里继续执行)
- 发生
return
的时候stack.pop
出栈后,将结果写入栈顶的结果字典
其他照抄就行
1 | def f_iter(n): |
完结,撒花🎉
另外:有一些函数编程语言,能将所有的递归调用转化成尾调用(非尾递归),这样就不会发生爆栈的问题,但是目前流行的大多数语言都是没有这个功能的
附加练习
有兴趣可以自己按步骤试一试, 如有见解,欢迎探讨👏
二叉树中序遍历
递归版本
1 | class Node: |
非递归版本
1 | class Node: |