python如何实现递归转非递归


Posted in Python onFebruary 25, 2021

先说总结,这种方案总的来说就是机械化的强转,时间复杂度和空间复杂度没什么变化,唯二的优点可能是1. 不会爆栈,2. 节省了函数调用的开销

而且最终产出的代码效果不那么美观,比较冗长

思路是:当发生递归调用时,模拟函数调用的 压栈 。并处理 入参 和 返回值 和 记录返回到当前栈的时候该继续从哪里执行

以如下递归( leetcode爬楼梯 )为例

def f(n):
 if n <= 2:
  return n
 return f(n - 1) + f(n - 2)

第一步:

将涉及到递归调用的,单独变成最简单的一行

def f(n):
 if n <= 2:
  return n
 a = f(n - 1)
 b = f(n - 2)
 return a + b

第二步:

我们需要模拟递归栈调用,当执行完递归回到当前栈的时候需要知道从哪里继续执行,所以需要一个flag标记,开始的时候为0,我们先手工标记一下,再后序转换的时候可以方便查看

def f(n):
 if n <= 2:
  return n
 a = f(n - 1)
 # flag1
 b = f(n - 2)
 # flag2
 return a + b

第三步: 

构建解题模版

def f_iter(n):
 stack = []
 # 入参,接收递归调用的(a,b), flag
 base_frame = [None, {'a': None, 'b': None}, 0]
 first_frame = [(n, 'a'), {}, 0]
 stack.append(base_frame)
 stack.append(first_frame)
 while len(stack) > 1:
  arg, local, flag = stack[-1]
  arg, aorb = arg
  if flag == 0:
   pass
  elif flag == 1:
   pass
  elif flag == 2:
   pass
 return stack[0][-2]['a']

first_frame = [(n, 'a'), {}, 0] 注意此时接收函数返回的时候为什么是一个字典,并且调用参数的时候传参多了一个'a',因为函数被递归调用了两次,分别得到一个a和b, 所以在返回的时候需要知道返回是给a还是给b, 如果只递归调用了一次,那么就不需要带上'a',返回的时候也不用是字典了,最后整个函数执行完成之后,base_frame里面就是最终的答案

第四步:

填充骨架,记住两点就可以了

函数调用的时候,先将当前栈的flag修改(等再次执行到当前栈的时候知道从哪里继续执行)
发生 return 的时候 stack.pop 出栈后,将结果写入栈顶的结果字典
其他照抄就行

def f_iter(n):
 stack = []
 # 入参,局部变量(a,b), flag
 base_frame = [None, {'a': None, 'b': None}, 0]
 first_frame = [(n, 'a'), {}, 0]
 stack.append(base_frame)
 stack.append(first_frame)
 while len(stack) > 1:
  arg, local, flag = stack[-1]
  arg, aorb = arg
  if flag == 0:
   if arg <= 2:
    stack.pop()
    stack[-1][-2][aorb] = arg
   else:
    stack[-1][-1] = 1
    new_frame = [(arg - 1, 'a'), {}, 0]
    stack.append(new_frame)
  elif flag == 1:
   stack[-1][-1] = 2
   new_frame = [(arg - 2, 'b'), {}, 0]
   stack.append(new_frame)
  elif flag == 2:
   a, b = local['a'], local['b']
   stack.pop()
   stack[-1][-2][aorb] = a + b
 return stack[0][-2]['a']

完结,撒花:tada:

另外:有一些函数编程语言,能将所有的递归调用转化成尾调用(非尾递归),这样就不会发生爆栈的问题,但是目前流行的大多数语言都是没有这个功能的

附加练习

有兴趣可以自己按步骤试一试, 如有见解,欢迎探讨:clap:

二叉树中序遍历

递归版本

class Node:
 def __init__(self, val):
  self.val = val
  self.left = None
  self.right = None

def list2tree(l):
 if len(l) == 1:
  return Node(l[0])
 mid = (len(l) - 1) >> 1
 root = Node(l[mid])
 root.left = list2tree(l[:mid])
 root.right = list2tree(l[mid + 1:])
 return root

def inorder_recursive(root):
 if not root:
  return []
 return inorder_recursive(root.left) + [root.val] + inorder_recursive(root.right)

l = list(range(1, 2 << 2))
tree = list2tree(l)

c = inorder_recursive(tree)
print(c)

非递归版本

class Node:
 def __init__(self, val):
  self.val = val
  self.left = None
  self.right = None

def list2tree(l):
 stack = []
 # (root, left_right), {'a':,'b':}, flag
 base_frame = [None, {}, 0]
 first_frame = [(l, 'a'), {}, 0]
 stack.append(base_frame)
 stack.append(first_frame)
 while len(stack) >1:
  cur = stack[-1]
  arg, local, flag = cur
  arg, aorb = arg
  mid = (len(arg) - 1) >> 1
  if flag == 0:
   if len(arg) == 1:
    stack.pop()
    stack[-1][-2][aorb] = Node(arg[0])
   else:
    stack[-1][-1] = 1
    new_frame = [(arg[:mid],'a'), {}, 0]
    stack.append(new_frame)
  elif flag == 1:
   stack[-1][-1] = 2
   new_frame = [(arg[mid+1:],'b'),{}, 0]
   stack.append(new_frame)
  elif flag == 2:
   left, right = local['a'], local['b']
   root = Node(arg[mid])
   root.left = left
   root.right = right
   stack.pop()
   stack[-1][-2][aorb] = root
 return stack[0][-2]['a']

def inorder_recursive(root):
 stack = []
 base_frame = [None, {}, 0]
 first_frame = [(root, 'a'), {'a': None, 'c': None}, 0]
 stack.append(base_frame)
 stack.append(first_frame)
 while len(stack) > 1:
  cur = stack[-1]
  arg, local, flag = cur
  arg, left_right = arg
  if flag == 0:
   if not arg:
    stack.pop()
    stack[-1][-2][left_right] = []
   else:
    stack[-1][-1] = 1
    new_frame = [(arg.left, 'a'), {}, 0]
    stack.append(new_frame)
  elif flag == 1:
   stack[-1][-1] = 2
   new_frame = [(arg.right, 'c'), {}, 0]
   stack.append(new_frame)
  elif flag == 2:
   b = [arg.val]
   ret = local['a'] + b + local['c']
   stack.pop()
   stack[-1][-2][left_right] = ret
 return stack[0][-2]['a']

l = list(range(1, 2 << 2))
tree = list2tree(l)

c = inorder_recursive(tree)
print(c)

以上就是python如何实现递归转非递归的详细内容,更多关于python 递归转非递归的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
python简单文本处理的方法
Jul 10 Python
django站点管理详解
Dec 12 Python
Ubuntu16.04/树莓派Python3+opencv配置教程(分享)
Apr 02 Python
python 获取url中的参数列表实例
Dec 18 Python
python 实现UTC时间加减的方法
Dec 31 Python
python 对多个csv文件分别进行处理的方法
Jan 07 Python
flask应用部署到服务器的方法
Jul 12 Python
python3 BeautifulSoup模块使用字典的方法抓取a标签内的数据示例
Nov 28 Python
浅析NumPy 切片和索引
Sep 02 Python
Pandas自定义选项option设置
Jul 25 Python
python自动化测试通过日志3分钟定位bug
Nov 20 Python
python画条形图的具体代码
Apr 20 Python
Python如何使用神经网络进行简单文本分类
Feb 25 #Python
Matlab使用Plot函数实现数据动态显示方法总结
Feb 25 #Python
如何用 Python 制作一个迷宫游戏
Feb 25 #Python
Django和Ueditor自定义存储上传文件的文件名
Feb 25 #Python
Python 图片处理库exifread详解
Feb 25 #Python
python中if嵌套命令实例讲解
Feb 25 #Python
Matplotlib animation模块实现动态图
Feb 25 #Python
You might like
星际争霸中的对战模式介绍
2020/03/04 星际争霸
thinkphp路由规则使用示例详解和伪静态功能实现(apache重写)
2014/02/24 PHP
PHP循环遍历数组的3种方法list()、each()和while总结
2014/11/19 PHP
PHP调用API接口实现天气查询功能的示例
2017/09/21 PHP
php workerman定时任务的实现代码
2018/12/23 PHP
用js+xml自动生成表格的东西
2006/12/21 Javascript
jQuery.extend()、jQuery.fn.extend()扩展方法示例详解
2014/05/08 Javascript
深入解析桶排序算法及Node.js上JavaScript的代码实现
2016/07/06 Javascript
jQuery插入节点和移动节点用法示例(insertAfter、insertBefore方法)
2016/09/08 Javascript
JavaScript定义全局对象的方法示例
2017/01/12 Javascript
详解wow.js中各种特效对应的类名
2017/09/13 Javascript
关于Vue组件库开发详析
2018/07/01 Javascript
JavaScript 中的无穷数(Infinity)详解
2020/02/13 Javascript
js中位数不足自动补位扩展padLeft、padRight实现代码
2020/04/06 Javascript
原生JavaScript实现贪吃蛇游戏
2020/11/04 Javascript
python数据清洗系列之字符串处理详解
2017/02/12 Python
Python编程之变量赋值操作实例分析
2017/07/24 Python
Python基于递归和非递归算法求两个数最大公约数、最小公倍数示例
2018/05/21 Python
python中实现字符串翻转的方法
2018/07/11 Python
python批量从es取数据的方法(文档数超过10000)
2018/12/27 Python
Python 解决相对路径问题:&quot;No such file or directory&quot;
2020/06/05 Python
python获取百度热榜链接的实例方法
2020/08/25 Python
HTML5调用手机摄像头拍照的实现思路及代码
2014/06/15 HTML / CSS
HTML5自定义元素播放焦点图动画的实现
2019/09/25 HTML / CSS
阿联酋优惠券服务:Living Kool
2019/12/12 全球购物
俄罗斯马克西多姆家居用品网上商店:Максидом
2020/02/06 全球购物
如何处理简单的PHP错误
2015/10/14 面试题
给酒店员工的表扬信
2014/01/11 职场文书
《小松树和大松树》教学反思
2014/02/20 职场文书
协议书模板
2014/04/23 职场文书
2015年财务试用期工作总结
2014/12/24 职场文书
搞笑欢迎词大全
2015/09/30 职场文书
2016春季校长开学典礼致辞
2015/11/26 职场文书
python基于OpenCV模板匹配识别图片中的数字
2021/03/31 Python
Python基础之条件语句详解
2021/06/16 Python
python非标准时间的转换
2021/07/25 Python