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 13 Python
Python的高级Git库 Gittle
Sep 22 Python
Python实现读取目录所有文件的文件名并保存到txt文件代码
Nov 22 Python
Python列表append和+的区别浅析
Feb 02 Python
如何使用python爬取csdn博客访问量
Feb 14 Python
详解Python的Lambda函数与排序
Oct 25 Python
Python实现树莓派WiFi断线自动重连的实例代码
Mar 16 Python
彻底搞懂Python字符编码
Jan 23 Python
关于pycharm中pip版本10.0无法使用的解决办法
Oct 10 Python
Python爬取阿拉丁统计信息过程图解
May 12 Python
Django model重写save方法及update踩坑详解
Jul 27 Python
python_tkinter弹出对话框创建
Mar 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
PHP UTF8编码内的繁简转换类
2009/07/20 PHP
PHP简单处理表单输入的特殊字符的方法
2016/02/03 PHP
Yii2搭建后台并实现rbac权限控制完整实例教程
2016/04/28 PHP
Laravel 中使用 Vue.js 实现基于 Ajax 的表单提交错误验证操作
2017/06/30 PHP
Laravel框架分页实现方法分析
2018/06/12 PHP
JavaScript脚本语言在网页中的简单应用
2007/05/13 Javascript
jQuery实现公告文字左右滚动的实例代码
2013/10/29 Javascript
js中将String转换为number以便比较
2014/07/08 Javascript
详解JavaScript ES6中的Generator
2015/07/28 Javascript
mui框架移动开发初体验详解
2017/10/11 Javascript
vue 多入口文件搭建 vue多页面搭建的实例讲解
2018/03/12 Javascript
浅析vue给不同环境配置不同打包命令
2018/08/17 Javascript
如何解决React官方脚手架不支持Less的问题(小结)
2018/09/12 Javascript
详解JS实现简单的时分秒倒计时代码
2019/04/25 Javascript
vue以组件或者插件的形式实现throttle或者debounce
2019/05/22 Javascript
vue如何搭建多页面多系统应用
2020/06/17 Javascript
VUE使用 wx-open-launch-app 组件开发微信打开APP功能
2020/08/11 Javascript
[02:39]DOTA2英雄基础教程 极限穿梭编织者
2013/12/05 DOTA
[03:05]《我与DAC》之xiao8:DAC与BG
2018/03/27 DOTA
[01:03:33]Alliance vs TNC 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/18 DOTA
Python程序中的观察者模式结构编写示例
2016/05/27 Python
python正则实现提取电话功能
2018/02/24 Python
Python3.6通过自带的urllib通过get或post方法请求url的实例
2018/05/10 Python
pandas把所有大于0的数设置为1的方法
2019/01/26 Python
使用已经得到的keras模型识别自己手写的数字方式
2020/06/29 Python
CSS3中新增的对文本和字体的设置
2020/02/03 HTML / CSS
英国第一家领先的在线处方眼镜零售商:Glasses Direct
2018/02/23 全球购物
制冷与电控专业应届生求职信
2013/11/11 职场文书
大学校运会广播稿
2014/02/03 职场文书
青奥会口号
2014/06/12 职场文书
政治学专业毕业生求职信
2014/08/11 职场文书
2014年作风建设心得体会
2014/10/22 职场文书
《家庭教育》读后感3篇
2019/12/18 职场文书
pdf论文中python画的图Type 3 fonts字体不兼容的解决方案
2021/04/24 Python
Python装饰器详细介绍
2022/03/25 Python
ECharts transform数据转换和dataZoom在项目中使用
2022/12/24 Javascript