在这个“生成括号”问题中,回溯是如何工作的?

Posted

技术标签:

【中文标题】在这个“生成括号”问题中,回溯是如何工作的?【英文标题】:How does backtracking work in this "Generate Parentheses" question? 【发布时间】:2021-04-27 22:41:10 【问题描述】:

我将下面的代码放在调试器中,并看到对于 n=2 的基本情况,当函数到达第 7 行时,return 语句,它返回并弹出最后 3 个括号。为什么会这样,我以为return语句应该是退出函数?

stack = []
res = []
n=2
def backtrack(openN, closedN):
  if openN == closedN ==n:
     res.append("".join(stack))
     return

  if openN < n:
     stack.append("(")
     backtrack(openN+1, closedN)
     stack.pop()

  if closedN < openN:
     stack.append(")")
     backtrack(openN, closedN+1)
     stack.pop()

backtrack(0,0)
print(res)

结果是:['(())', '()()']

【问题讨论】:

请提供预期的minimal, reproducible example (MRE)。我们应该能够复制和粘贴您的代码的连续块,执行该文件,并重现您的问题以及跟踪问题点的输出。这让我们可以根据您的测试数据和所需的输出来测试我们的建议。显示中间结果与您的预期不同的地方。 向我们确切地向我们展示您在跟踪此代码中的中间表达式时所不理解的内容。 “向我解释这个代码块”超出了 Stack Overflow 的范围:解构复合表达式并告诉我们你对其中一两个操作的不理解之处。您发布的代码定义了一个您从不实例化的类——代码在定义后简单地退出。没有显示多个括号、弹出或任何此类执行的输出。 函数是递归的。它退出当前调用,但不退出调用者。 @Barmar 您能否对此进行扩展。我不知道你说的调用和调用者是什么意思。 你了解递归的一般概念吗?当backtrack() 递归调用自身并返回时,它又回到了原来的调用中。 【参考方案1】:

添加调试打印语句以跟踪操作是有指导意义的。

我们调用generateParenthesis。这调用回溯,默认参数 S = []。我们到达第二个if,附加一个 ( 并再次调用回溯。这采用相同的路径,并再次调用回溯。现在调用堆栈是:

generateParenthesis
  backtrack([], 0, 0)
    backtrac(['('], 1, 0)
      backtrack(['(','('], 2, 0)

这次left不小于n,所以我们取第三个if。我们附加一个右括号并再次调用回溯。这采用相同的路径,所以我们最终得到:

generateParenthesis
  backtrack([], 0, 0)
    backtrack(['('], 1, 0)
      backtrack(['(','('], 2, 0)
        backtrack(['(','(',')'], 2, 1)
          backtrack(['(','(',')',')'], 2, 2)

此时,len(S) == 2 * n 为真,所以我们采用第一个选项。我们追加到ans 列表并返回,但这只从最里面的调用返回。

generateParenthesis
  backtrack([], 0, 0)
    backtrack(['('], 1, 0)
      backtrack(['(','('], 2, 0)
        backtrack(['(','(',')'], 2, 1)

那个电话是在第三个if中间留下的。它从 S 弹出并返回,给我们留下

generateParenthesis
  backtrack([], 0, 0)
    backtrack(['('], 1, 0)
      backtrack(['(','('], 2, 0)

以此类推,直到最后的调用返回。

【讨论】:

以上是关于在这个“生成括号”问题中,回溯是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

22. 括号生成(回溯+剪枝)

22. 括号生成(回溯+剪枝)

22. 括号生成(回溯+剪枝)

[LeetCode] 22. 括号生成(回溯/DP)

22. 括号生成-全排列-回溯

2021/6/13 刷题笔记括号生成与回溯法(深度优先遍历)