【问题标题】:replacing arbitrary recursion using a stack?使用堆栈替换任意递归?
【发布时间】:2015-03-03 11:00:59
【问题描述】:

使用堆栈实现递归似乎很明显。但是,一般递归究竟是如何使用堆栈实现的呢?例如,如果我们有一个递归的自定义函数,我们可以只使用堆栈和迭代来重写代码吗?

这是一个例子(我在另一篇文章中给出了错误的例子):

def recursiveCall(node):
    if node==None:
        return (0,True)
    (left,lstatus) = recursiveCall(node.left)
    (right,rstatus) = recursiveCall(node.right)
    if lstatus==True and rstatus==True:
        if abs(left-right)<=1:
            return (max(left,right)+1,True)
        else:
            return (0,False)
    else:
        return (0,False)

或更简单的:

def recursiveInorder(node):
    if node==None:
        return 
    recursiveInorder(node.left)
    print(node.val)
    recursiveInorder(node.right)

您如何使用堆栈实现这种递归?请注意,我并不是要求对上述两个示例进行迭代解决方案。我相信一定有。但我想那些迭代解决方案并不是试图使用堆栈来重现递归机制。我希望看到,如果可能的话,这些递归可以完全被自定义编码的堆栈机制所取代(基本上是使隐式递归机制嵌入编译器或任何显式的机制)。

我认为需要找到一种方法来跟踪和恢复程序状态、局部变量等? 谢谢。


节点类定义为:

class node:
   def __init__(self,x):
      self.val=x
      self.left=None
      self.right=None

【问题讨论】:

  • Christian Thx.. 但我无法阅读帖子中使用的大部分语言。也许我确实需要看一个真实的例子。
  • 对递归函数的调用本质上是压入堆栈。递归调用的返回是从堆栈中弹出。当堆栈不为空时程序继续运行。
  • 但我认为您还需要将其他内容推入堆栈。还 。两个分行怎么样?我怎么知道在哪里恢复?在中序示例中,您在中间恢复
  • 如果你在 Python 上因为递归太深而导致堆栈溢出,而你的程序相对简单,一个友好的建议:尝试用 C/C++ 或 Java/Scala 重写它。与尝试绕过 Python 中的限制相比,这很可能会花费您更少的时间。

标签: python recursion


【解决方案1】:

基本上,在模拟递归调用时,您需要压栈局部变量以及返回后应该恢复执行的点。

我将在此处通过编号的 cmets 指示相关的执行点。将它们视为goto 标签。

def recursiveInorder(node):
    #0
    if node==None:
        return 
    recursiveInorder(node.left)
    #1
    print(node.val)
    recursiveInorder(node.right)
    #2
    return

现在,我们可以使用if-elif 语句来模拟goto 语句:

def in_order(node):
    stack = [(None, None)] #sentinel
    goto = 0
    while stack:
        if goto == 0:
            if node is None:
                #return
                (node, goto) = stack.pop()
            else:
                #push state and recurse left
                stack.append((node, goto+1))
                (node, goto) = (node.left, 0)
        elif goto == 1:
            print(node.val)
            #push state and recurse right
            stack.append((node, goto+1))
            (node, goto) = (node.right, 0)
        else: 
            #return
            (node, goto) = stack.pop()

最后,(None, None) 将被弹出,但由于while 循环结束,因此这些值永远不会被使用。


上面的代码是直接转换的结果。接下来,我们可以应用各种优化来简化它。

最后一个else 分支没有做有用的工作。如果我们还删除将带我们到那里的推动,我们可以删除它。

def in_order(node):
    stack = [(None, None)] #sentinel
    goto = 0
    while stack:
        if goto == 0:
            if node is None:
                #return
                (node, goto) = stack.pop()
            else:
                #push state and recurse left
                stack.append((node, goto+1))
                (node, goto) = (node.left, 0)
        else:
            print(node.val)
            #recurse right
            (node, goto) = (node.right, 0)

现在入栈的goto值始终为1。我们只需要将node入栈,出栈时赋值goto = 1即可。

def in_order(node):
    stack = [None] #sentinel
    goto = 0
    while stack:
        if goto == 0:
            if node is None:
                #return
                (node, goto) = (stack.pop(), 1)
            else:
                #push state and recurse left
                stack.append(node)
                (node, goto) = (node.left, 0)
        else:
            print(node.val)
            #recurse right
            (node, goto) = (node.right, 0)

如果我们将内部 if 更改为 while 循环...

def in_order(node):
    stack = [None] #sentinel
    goto = 0
    while stack:
        if goto == 0:
            while node is not None:
                #push state and recurse left
                stack.append(node)
                node = node.left
            #return
            (node, goto) = (stack.pop(), 1)
        else:
            print(node.val)
            #recurse right
            (node, goto) = (node.right, 0)

...我们看到在if 语句的每个分支之后,我们都想去另一个分支,直到最后我们弹出标记值。如果我们在中间添加一个空堆栈检查,我们可以消除gotoif 语句。如果我们将检查放在弹出之前,我们就不再需要堆栈中的哨兵了。

def in_order(node):
    stack = []
    while True:
        while node is not None:
            stack.append(node)
            node = node.left
        if stack:
            node = stack.pop()
            print(node.val)
            node = node.right
        else:
            return

现在代码看起来简洁明了。

【讨论】:

    猜你喜欢
    • 2015-03-02
    • 2015-10-26
    • 2011-04-29
    • 1970-01-01
    • 2014-12-10
    • 1970-01-01
    • 1970-01-01
    • 2020-07-20
    • 2011-11-24
    相关资源
    最近更新 更多