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 02 Python
Python爬虫爬取美剧网站的实现代码
Sep 03 Python
python批量添加zabbix Screens的两个脚本分享
Jan 16 Python
Python3安装Pillow与PIL的方法
Apr 03 Python
python如何给字典的键对应的值为字典项的字典赋值
Jul 05 Python
python pandas移动窗口函数rolling的用法
Feb 29 Python
解决Django中checkbox复选框的传值问题
Mar 31 Python
python 使用xlsxwriter循环向excel中插入数据和图片的操作
Jan 01 Python
OpenCV-Python实现怀旧滤镜与连环画滤镜
Jun 09 Python
一文搞懂Python Sklearn库使用
Aug 23 Python
教你使用Python获取QQ音乐某个歌手的歌单
Apr 03 Python
Python中生成随机数据安全性、多功能性、用途和速度方面进行比较
Apr 14 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下获取客户端ip地址的函数
2010/03/15 PHP
PHP文章按日期(月日)SQL归档语句
2012/11/29 PHP
php类常量用法实例分析
2015/07/09 PHP
css transform 3D幻灯片特效实现步骤解读
2013/03/27 Javascript
select标签模拟/美化方法采用JS外挂式插件
2013/04/01 Javascript
jQuery使用slideUp方法实现控制元素缓慢收起
2015/03/27 Javascript
AngularJS中监视Scope变量以及外部调用Scope方法
2016/01/23 Javascript
原生js实现图片层叠轮播切换效果
2016/02/02 Javascript
JavaScript DOM 对象深入了解
2016/07/20 Javascript
jfinal与bootstrap的登出实战详解
2017/11/27 Javascript
vue+axios新手实践实现登陆的示例代码
2018/06/06 Javascript
vue-cli整合vuex的时候,修改actions和mutations,实现热部署的方法
2018/09/19 Javascript
详解easyui 切换主题皮肤
2019/04/04 Javascript
JS多个表单数据提交下的serialize()应用实例分析
2019/08/27 Javascript
微信小程序实现蓝牙打印
2019/09/23 Javascript
使用node-media-server搭建一个简易的流媒体服务器
2021/01/20 Javascript
常用python数据类型转换函数总结
2014/03/11 Python
Python实现批量读取word中表格信息的方法
2015/07/30 Python
浅谈Python 参数与变量
2020/06/20 Python
浅谈多卡服务器下隐藏部分 GPU 和 TensorFlow 的显存使用设置
2020/06/30 Python
详解python datetime模块
2020/08/17 Python
Python计算矩阵的和积的实例详解
2020/09/10 Python
python3实现飞机大战
2020/11/29 Python
HTML5 CSS3给网站设计带来出色效果
2009/07/16 HTML / CSS
NUK奶瓶美国官网:NUK美国
2016/09/26 全球购物
德国低价购买灯具和家具网站:Style-home.de
2016/11/25 全球购物
试述DBMS的主要功能
2016/11/13 面试题
教师年终个人自我评价
2013/10/04 职场文书
计算机专业学生求职信分享
2013/12/15 职场文书
2014年乡镇植树节活动方案
2014/02/28 职场文书
重阳节慰问信
2015/02/15 职场文书
董事长开业致辞
2015/07/29 职场文书
心理健康教育培训研修感言
2015/11/18 职场文书
python 安全地删除列表元素的方法
2022/03/16 Python
MySQL中一条SQL查询语句是如何执行的
2022/04/08 MySQL
Nginx本地配置SSL访问的实例教程
2022/05/30 Servers