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捕捉和模拟鼠标事件的方法
Jun 03 Python
Python学习笔记之解析json的方法分析
Apr 21 Python
基于使用paramiko执行远程linux主机命令(详解)
Oct 16 Python
flask + pymysql操作Mysql数据库的实例
Nov 13 Python
Python获取二维矩阵每列最大值的方法
Apr 03 Python
使用matplotlib画散点图的方法
May 25 Python
Python实现随机漫步功能
Jul 09 Python
Python GUI布局尺寸适配方法
Oct 11 Python
利用python实现在微信群刷屏的方法
Feb 21 Python
梅尔频率倒谱系数(mfcc)及Python实现
Jun 18 Python
Python OpenCV视频截取并保存实现代码
Nov 30 Python
基于tensorflow __init__、build 和call的使用小结
Feb 26 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
yii2.0实现验证用户名与邮箱功能
2015/12/22 PHP
php实现socket推送技术的示例
2017/12/20 PHP
Laravel框架实现修改登录和注册接口数据返回格式的方法
2018/08/17 PHP
用js实现下载远程文件并保存在本地的脚本
2008/05/06 Javascript
Extjs学习笔记之八 继承和事件基础
2010/01/08 Javascript
javascript offsetX与layerX区别
2010/03/12 Javascript
利用JavaScript检测CPU使用率自己写的
2014/03/22 Javascript
javascript查询字符串参数的方法
2015/01/28 Javascript
jQuery scrollFix滚动定位插件
2015/04/01 Javascript
JavaScript 事件对象介绍
2015/04/13 Javascript
javascript中call apply 与 bind方法详解
2016/03/10 Javascript
AngularJS基础 ng-open 指令简单实例
2016/08/02 Javascript
Angular 通过注入 $location 获取与修改当前页面URL的实例
2017/05/31 Javascript
VUE页面中加载外部HTML的示例代码
2017/09/20 Javascript
javascript少儿编程关于返回值的函数内容
2018/05/27 Javascript
[02:40]DOTA2英雄基础教程 巨牙海民
2013/12/23 DOTA
Python下使用Psyco模块优化运行速度
2015/04/05 Python
Python使用poplib模块和smtplib模块收发电子邮件的教程
2016/07/02 Python
python 循环遍历字典元素的简单方法
2016/09/11 Python
Python通过matplotlib绘制动画简单实例
2017/12/13 Python
详解python 模拟豆瓣登录(豆瓣6.0)
2019/04/18 Python
python3发送邮件需要经过代理服务器的示例代码
2019/07/25 Python
python识别文字(基于tesseract)代码实例
2019/08/24 Python
python基于TCP实现的文件下载器功能案例
2019/12/10 Python
Chemist Warehouse官方海外旗舰店:澳洲第一连锁大药房
2017/08/25 全球购物
美国宠物护理专家:Revival Animal Health
2020/01/05 全球购物
求网格中的黑点分布
2013/11/06 面试题
写给学生的新学期寄语
2014/01/18 职场文书
小学领导班子对照材料
2014/08/23 职场文书
建设办主任四风问题整改思路和措施
2014/09/20 职场文书
群众路线教育实践活动整改落实情况汇报
2014/10/28 职场文书
会计试用期自我评价
2015/03/10 职场文书
婚礼嘉宾致辞
2015/07/28 职场文书
朋友离别感言
2015/08/04 职场文书
Spring Boot DevTools 全局配置学习指南
2022/03/31 Java/Android
Redis基本数据类型String常用操作命令
2022/06/01 Redis