是否可以将此递归解决方案(打印括号)转换为迭代版本?

时间:2011-02-23 06:32:18

标签: algorithm recursion iteration tail-recursion

我需要打印不同版本的打印有效标签“<”和“>”给出标签出现的次数,下面是使用递归的python中的解决方案。

def genBrackets(c):
    def genBracketsHelper(r,l,currentString):
        if l > r or r == -1 or l == -1:
            return
        if r == l and r == 0:
            print currentString
        genBracketsHelper(r,l-1, currentString + '<')
        genBracketsHelper(r-1,l, currentString + '>')
        return
    genBracketsHelper(c, c, '')

#display options with 4 tags     
genBrackets(4)

我很难理解这一点,并希望尝试将其转换为迭代版本,但我没有取得任何成功。

根据这个帖子:Can every recursion be converted into iteration? - 它看起来应该是可能的,唯一的例外似乎是Ackermann函数。

如果有人知道如何查看Eclipse中维护的“堆栈” - 那也将不胜感激。

PS。这不是一个功课问题 - 我只是想更好地理解递归到迭代转换。

由Matthieu M编辑。输出示例,以便更好地实现可视化:

>>> genBrackets(3)
<<<>>>
<<><>>
<<>><>
<><<>>
<><><>

4 个答案:

答案 0 :(得分:4)

我尝试保持与代码基本相同的结构,但是使用显式堆栈而不是函数调用genBracketsHelper

def genBrackets(c=1):
    # genBracketsStack is a list of tuples, each of which
    # represents the arguments to a call of genBracketsHelper
    # Push the initial call onto the stack:
    genBracketsStack = [(c, c, '')]
    # This loop replaces genBracketsHelper itself
    while genBracketsStack != []:
        # Get the current arguments (now from the stack)
        (r, l, currentString) = genBracketsStack.pop()
        # Basically same logic as before
        if l > r or r == -1 or l == -1:
            continue # Acts like return
        if r == l and r == 0:
            print currentString
        # Recursive calls are now pushes onto the stack
        genBracketsStack.append((r-1,l, currentString + '>'))
        genBracketsStack.append((r,l-1, currentString + '<'))
        # This is kept explicit since you had an explicit return before
        continue

genBrackets(4)

请注意,我使用的转换依赖于函数末尾的所有递归调用;如果不是这样的话代码会更复杂。

答案 1 :(得分:2)

你没有堆叠就问过这个问题。

此算法遍历整个解决方案空间,因此它比原始版本做了更多的工作,但它基本上是相同的概念:

  • 每个字符串在您的语法中都有一个可能后缀的树
  • 因为只有两个令牌,所以它是二叉树
  • 树的深度总是c * 2,所以......
  • 树上必须有2 **(c * 2)条路径

由于每个路径都是一系列二元决策,因此路径对应于0到2 **(c * 2)-1之间整数的二进制表示。

所以:只需遍历这些数字,看看二进制表示是否对应于平衡字符串。 :)

def isValid(string):
    """
    True if and only if the string is balanced.
    """
    count = { '<': 0, '>':0 }
    for char in string:
        count[char] += 1
        if count['>'] > count['<']:
            return False # premature closure

    if count['<'] != count['>']:
        return False # unbalanced
    else:
        return True


def genBrackets(c):
    """
    Generate every possible combination and test each one.
    """
    for i in range(0, 2**(c*2)):
        candidate = bin(i)[2:].zfill(8).replace('0','<').replace('1','>')
        if isValid(candidate):
            print candidate

答案 2 :(得分:1)

通常,递归会创建一个调用树,根是原始调用,而叶子是不递归的调用。

退化情况是每个调用仅执行另一个调用,在这种情况下,树退化为一个简单列表。然后通过使用堆栈简单地实现向迭代的转换,如@Jeremiah所示。

在更一般的情况下,就像这里一样,当每次通话比一次通话执行更多(严格)时。你获得了一棵真正的树,因此有几种方法可以遍历它。

如果使用队列而不是堆栈,则执行广度优先遍历。 @Jeremiah提出了一个我不知道名字的遍历。典型的“递归”顺序通常是深度优先遍历。

典型递归的主要优点是堆栈的长度不会增长太多,所以你应该首先瞄准深度优先......如果复杂性没有压倒你:)

我建议首先编写一个树的深度优先遍历,一旦完成,将其调整到你的算法应该相当简单。

编辑:由于我有一段时间,我编写了Python Tree Traversal,这是一个典型的例子:

class Node:
  def __init__(self, el, children):
    self.element = el
    self.children = children

  def __repr__(self):
    return 'Node(' + str(self.element) + ', ' + str(self.children) + ')'

def depthFirstRec(node):
  print node.element

  for c in node.children: depthFirstRec(c)

def depthFirstIter(node):
  stack = [([node,], 0), ]

  while stack != []:
    children, index = stack.pop()

    if index >= len(children): continue
    node = children[index]

    print node.element

    stack.append((children, index+1))
    stack.append((node.children, 0))

请注意,由于需要记住我们当前访问的孩子的索引,因此堆栈管理稍微复杂。

按照深度优先顺序对算法进行调整:

def generateBrackets(c):
  # stack is a list of pairs children/index
  stack = [([(c,c,''),], 0), ]

  while stack != []:
    children, index = stack.pop()

    if index >= len(children): continue  # no more child to visit at this level
    stack.append((children, index+1))    # register next child at this level

    l, r, current = children[index]

    if r == 0 and l == 0: print current

    # create the list of children of this node
    # (bypass if we are already unbalanced)
    if l > r: continue

    newChildren = []
    if l != 0: newChildren.append((l-1, r, current + '<'))
    if r != 0: newChildren.append((l, r-1, current + '>'))
    stack.append((newChildren, 0))

我刚刚意识到存储索引有点“太”复杂,因为我从不回访。因此,简单的解决方案在于删除我不再需要的列表元素,将列表视为队列(实际上,堆栈就足够了)!

这适用于最小转换。

def generateBrackets2(c):
  # stack is a list of queues of children
  stack = [[(c,c,''),], ]

  while stack != []:
    children = stack.pop()

    if children == []: continue  # no more child to visit at this level
    stack.append(children[1:])   # register next child at this level

    l, r, current = children[0]

    if r == 0 and l == 0: print current

    # create the list of children of this node
    # (bypass if we are already unbalanced)
    if l > r: continue

    newChildren = []
    if l != 0: newChildren.append((l-1, r, current + '<'))
    if r != 0: newChildren.append((l, r-1, current + '>'))
    stack.append(newChildren)

答案 3 :(得分:0)

def genBrackets(c):
    stack = [(c, c, '')]
    while stack:
        right, left, currentString = stack.pop()
        if left > right or right == -1 or left == -1:
            pass
        elif right == left and right == 0:
            print currentString
        else:
            stack.append((right, left-1, currentString + '<'))
            stack.append((right-1, left, currentString + '>'))

输出顺序不同,但结果应该相同。