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 代码优化详解
Oct 27 Python
Python数据结构与算法之列表(链表,linked list)简单实现
Oct 30 Python
详谈python在windows中的文件路径问题
Apr 28 Python
Pycharm 设置自定义背景颜色的图文教程
May 23 Python
Python3实现的爬虫爬取数据并存入mysql数据库操作示例
Jun 06 Python
如何利用Python模拟GitHub登录详解
Jul 15 Python
Python基于Tensor FLow的图像处理操作详解
Jan 15 Python
python列表的逆序遍历实现
Apr 20 Python
基于打开pycharm有带图片md文件卡死问题的解决
Apr 24 Python
python 用opencv实现图像修复和图像金字塔
Nov 27 Python
基于Django快速集成Echarts代码示例
Dec 01 Python
python中温度单位转换的实例方法
Dec 27 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中ADODB类详解
2008/03/25 PHP
yii框架builder、update、delete使用方法
2014/04/30 PHP
CI框架中通过hook的方式实现简单的权限控制
2015/01/07 PHP
WordPress中自定义后台管理界面配色方案的小技巧
2015/12/29 PHP
Laravel 自动转换长整型雪花 ID 为字符串的实现
2020/10/27 PHP
Ajax,UTF-8还是GB2312 eval 还是execScript
2008/11/13 Javascript
JS面向对象编程之对象使用分析
2010/08/19 Javascript
jQuery 文本框得失焦点的简单实例
2014/02/19 Javascript
跟我学习javascript解决异步编程异常方案
2015/11/23 Javascript
Bootstrap基本组件学习笔记之分页(12)
2016/12/08 Javascript
vue2 如何实现div contenteditable=“true”(类似于v-model)的效果
2017/02/08 Javascript
浅谈键盘上回车按钮的js触发事件
2017/02/13 Javascript
微信小程序中post方法与get方法的封装
2017/09/26 Javascript
如何优雅地在vue中添加权限控制示例详解
2019/03/07 Javascript
微信小程序tabBar设置实例解析
2019/11/14 Javascript
python中定义结构体的方法
2013/03/04 Python
python操作excel的方法(xlsxwriter包的使用)
2018/06/11 Python
python 实现将txt文件多行合并为一行并将中间的空格去掉方法
2018/12/20 Python
python提取照片坐标信息的实例代码
2019/08/14 Python
Guess欧洲官网:美国服饰品牌
2019/08/06 全球购物
CAT鞋加拿大官网:CAT Footwear加拿大
2020/08/05 全球购物
新加坡最早生产电动滑板车的制造商之一:FunsToTheFore
2020/09/08 全球购物
行政总监岗位职责
2013/12/05 职场文书
行政主管职责范本
2014/03/07 职场文书
给校长的建议书600字
2014/05/15 职场文书
煤矿安全承诺书
2014/05/22 职场文书
小学先进集体事迹材料
2014/05/31 职场文书
建筑工地宣传标语
2014/06/18 职场文书
公司门卫岗位职责范本
2014/07/08 职场文书
龙潭大峡谷导游词
2015/02/10 职场文书
计划生育目标责任书
2015/05/09 职场文书
雷锋的观后感
2015/06/10 职场文书
农村婚庆主持词
2015/06/29 职场文书
诚信教育主题班会
2015/08/13 职场文书
掌握这项技巧,一年阅读300本书不是梦
2019/09/12 职场文书
使用react-virtualized实现图片动态高度长列表的问题
2021/05/28 Javascript