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神器对付12306变态验证码
Jan 05 Python
十条建议帮你提高Python编程效率
Feb 16 Python
Python整型运算之布尔型、标准整型、长整型操作示例
Jul 21 Python
numpy数组拼接简单示例
Dec 15 Python
Python爬虫使用Selenium+PhantomJS抓取Ajax和动态HTML内容
Feb 23 Python
基于python requests库中的代理实例讲解
May 07 Python
Python使用requests提交HTTP表单的方法
Dec 26 Python
Python元组知识点总结
Feb 18 Python
Python2和3字符编码的区别知识点整理
Aug 08 Python
python绘制BA无标度网络示例代码
Nov 21 Python
Python Sympy计算梯度、散度和旋度的实例
Dec 06 Python
浅谈TensorFlow之稀疏张量表示
Jun 30 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
一些操作和快捷键的理解和讨论
2020/03/04 星际争霸
PHP文本数据库的搜索方法
2006/10/09 PHP
php 变量未定义等错误的解决方法
2011/01/12 PHP
php框架CodeIgniter主从数据库配置方法分析
2018/05/25 PHP
Thinkphp5+Redis实现商品秒杀代码实例讲解
2020/12/29 PHP
javascript编程起步(第四课)
2007/01/10 Javascript
自制基于jQuery的智能提示插件一枚
2011/02/18 Javascript
基于JQuery实现CheckBox全选全不选
2011/06/27 Javascript
文本框获得焦点和失去焦点的判断代码
2012/03/18 Javascript
原生javascript实现图片弹窗交互效果
2015/01/12 Javascript
jQuery操作基本控件方法实例分析
2015/12/31 Javascript
jQuery事件委托之Safari
2016/07/05 Javascript
详谈angularjs中路由页面强制更新的问题
2017/04/24 Javascript
echart简介_动力节点Java学院整理
2017/08/11 Javascript
JS组件系列之Gojs组件 前端图形化插件之利器
2017/11/29 Javascript
layui 监听select选择 获取当前select的ID名称方法
2019/09/24 Javascript
JS面向对象之多选框实现
2020/01/17 Javascript
Vue.js中Line第三方登录api的实现代码
2020/06/29 Javascript
[01:53]DOTA2超级联赛专访Zhou 五年职业青春成长
2013/05/29 DOTA
[01:00]一分钟回顾2018DOTA2亚洲邀请赛现场活动
2018/04/07 DOTA
[01:03:09]完美世界DOTA2联赛PWL S2 Forest vs SZ 第二场 11.25
2020/11/26 DOTA
Python正则抓取网易新闻的方法示例
2017/04/21 Python
利用python实现简单的循环购物车功能示例代码
2017/07/05 Python
Python数据分析之双色球统计两个红和蓝球哪组合比例高的方法
2018/02/03 Python
Pycharm无法显示动态图片的解决方法
2018/10/28 Python
Pycharm之快速定位到某行快捷键的方法
2019/01/20 Python
django框架基于模板 生成 excel(xls) 文件操作示例
2019/06/19 Python
django 做 migrate 时 表已存在的处理方法
2019/08/31 Python
Python3.9 beta2版本发布了,看看这7个新的PEP都是什么
2020/06/10 Python
医科大学生毕业的自我评价分享
2013/11/12 职场文书
英语分层教学实施方案
2014/06/15 职场文书
汽车转让协议书
2015/01/29 职场文书
大学毕业生个人总结
2015/02/28 职场文书
保险公司客户经理岗位职责
2015/04/09 职场文书
演讲比赛主持词
2015/06/29 职场文书
Vue router配置与使用分析讲解
2022/12/24 Vue.js