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中针对函数处理的特殊方法
Mar 06 Python
Python编程之属性和方法实例详解
May 19 Python
Python实现将DOC文档转换为PDF的方法
Jul 25 Python
Python 探针的实现原理
Apr 23 Python
教你用Type Hint提高Python程序开发效率
Aug 08 Python
关于Python元祖,列表,字典,集合的比较
Jan 06 Python
python版微信跳一跳游戏辅助
Jan 11 Python
python多环境切换及pyenv使用过程详解
Sep 27 Python
基于Python第三方插件实现西游记章节标注汉语拼音的方法
May 22 Python
快速了解Python开发环境Spyder
Jun 29 Python
Python深度学习之Pytorch初步使用
May 20 Python
详解PyTorch模型保存与加载
Apr 28 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中的Class的几点个人看法
2006/10/09 PHP
如何使用php实现评委评分器
2015/07/31 PHP
Yii操作数据库实现动态获取表名的方法
2016/03/29 PHP
解决在Laravel 中处理OPTIONS请求的问题
2019/10/11 PHP
关于恒等于(===)和非恒等于(!==)
2007/08/20 Javascript
JS中引用百度地图并将百度地图的logo和信息去掉
2013/09/29 Javascript
jQuery aminate方法定位到页面具体位置
2013/12/26 Javascript
js数组方法扩展实现数组统计函数
2014/04/09 Javascript
JavaSript中变量的作用域闭包的深入理解
2014/05/12 Javascript
根据user-agent判断蜘蛛代码黑帽跳转代码(js版与php版本)
2015/09/14 Javascript
jQuery操作iframe中js函数的方法小结
2016/07/06 Javascript
js 中获取制定的cook信息实现方法
2016/11/19 Javascript
Restify中接入Socket.io报Error:Can’t set headers的错误解决
2017/03/28 Javascript
jQuery插件FusionCharts绘制的3D环饼图效果示例【附demo源码】
2017/04/02 jQuery
jQuery EasyUI 选项卡面板tabs的使用实例讲解
2017/12/25 jQuery
Vue使用json-server进行后端数据模拟功能
2018/04/17 Javascript
angular2路由之routerLinkActive指令【推荐】
2018/05/30 Javascript
解决vue2 在mounted函数无法获取prop中的变量问题
2018/11/15 Javascript
微信小程序在text文本实现多种字体样式
2019/11/08 Javascript
爬山算法简介和Python实现实例
2014/04/26 Python
Ubuntu下安装PyV8
2016/03/13 Python
selenium+python 对输入框的输入处理方法
2018/10/11 Python
python实现根据指定字符截取对应的行的内容方法
2018/10/23 Python
Python爬虫scrapy框架Cookie池(微博Cookie池)的使用
2021/01/13 Python
python脚本使用阿里云slb对恶意攻击进行封堵的实现
2021/02/04 Python
来自世界各地的优质葡萄酒:VineShop24
2018/07/09 全球购物
澳大利亚和新西兰最大的在线旅行社之一:Aunt Betty
2019/08/07 全球购物
历史学专业个人的自我评价
2013/10/13 职场文书
总经理助理岗位职责
2013/11/08 职场文书
仓库管理专业个人的自我评价
2013/12/30 职场文书
冰淇淋开店创业计划书
2014/02/01 职场文书
2014年社会实践活动总结范文
2014/04/29 职场文书
天下第一关导游词
2015/02/06 职场文书
英文慰问信
2015/02/14 职场文书
安全第一课观后感
2015/06/18 职场文书
刚学完怎么用Python实现定时任务,转头就跑去撩妹!
2021/06/05 Python