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爬虫之打包生成exe文件
Nov 06 Python
在Python中使用PIL模块处理图像的教程
Apr 29 Python
利用Python将每日一句定时推送至微信的实现方法
Aug 13 Python
Python多线程原理与用法详解
Aug 20 Python
Python 微信爬虫完整实例【单线程与多线程】
Jul 06 Python
python字符串的拼接方法总结
Nov 18 Python
python时间与Unix时间戳相互转换方法详解
Feb 13 Python
python pymysql链接数据库查询结果转为Dataframe实例
Jun 05 Python
解决Keras的自定义lambda层去reshape张量时model保存出错问题
Jul 01 Python
python用tkinter实现一个简易能进行随机点名的界面
Sep 27 Python
python如何提升爬虫效率
Sep 27 Python
如何查看python关键字
Jan 17 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
PHP 简单数组排序实现代码
2009/08/05 PHP
PHP中4个加速、缓存扩展的区别和选用建议
2014/03/12 PHP
PHP基于CURL进行POST数据上传实例
2014/11/10 PHP
PHP实现非阻塞模式的方法分析
2018/07/26 PHP
CutePsWheel javascript libary 控制输入文本框为可使用滚轮控制的js库
2010/02/07 Javascript
JavaScript聚焦于第一个字段的代码
2010/10/15 Javascript
Extjs显示从数据库取出时间转换JSON后的出现问题
2012/11/20 Javascript
JavaScript实现跨浏览器的添加及删除事件绑定函数实例
2015/08/04 Javascript
基于JavaScript实现Json数据根据某个字段进行排序
2015/11/24 Javascript
JavaScript中动态向表格添加数据
2017/01/24 Javascript
JavaScript轮播图简单制作方法
2017/02/20 Javascript
jquery拼接ajax 的json和字符串拼接的方法
2017/03/11 Javascript
基于vue的fullpage.js单页滚动插件
2017/03/20 Javascript
JavaScript正则表达式函数总结(常用)
2018/02/22 Javascript
vuejs使用axios异步访问时用get和post的实例讲解
2018/08/09 Javascript
取消Bootstrap的dropdown-menu点击默认关闭事件方法
2018/08/10 Javascript
js计算最大公约数和最小公倍数代码实例
2019/09/11 Javascript
vue 使用饿了么UI仿写teambition的筛选功能
2021/03/01 Vue.js
探寻python多线程ctrl+c退出问题解决方案
2014/10/23 Python
python读文件保存到字典,修改字典并写入新文件的实例
2018/04/23 Python
对pyqt5多线程正确的开启姿势详解
2019/06/14 Python
Python实现中值滤波去噪方式
2019/12/18 Python
Python绘制全球疫情变化地图的实例代码
2020/04/20 Python
如何用python爬取微博热搜数据并保存
2021/02/20 Python
css3针对移动端卡顿问题的解决(动画性能优化)
2020/02/14 HTML / CSS
让IE支持HTML5的方法
2012/12/11 HTML / CSS
法国奢华女性时尚配饰网上商店:Monnier Frères
2016/08/27 全球购物
有机童装:Toby Tiger
2018/05/23 全球购物
全球速卖通俄罗斯站:AliExpress俄罗斯
2019/06/17 全球购物
四种会话跟踪技术
2015/05/20 面试题
人力资源管理专业毕业生自我评价
2013/09/21 职场文书
民族团结先进个人事迹材料
2014/06/02 职场文书
2014年社区党建工作总结
2014/11/11 职场文书
孩子满月酒答谢词
2015/09/30 职场文书
redis cluster支持pipeline的实现思路
2021/06/23 Redis
Golang中channel的原理解读(推荐)
2021/10/16 Golang