递归调用栈执行逻辑:有效括号生成回溯后的跳转疑问
递归生成有效括号的调用栈解析
先明确递归调用栈的核心逻辑:每调用一次backtrack函数,系统就会在调用栈里压入一个新的「栈帧」,栈帧里存着当前的open_count、close_count、combination参数,以及当前执行到哪一行的返回地址。当函数执行到return时,当前栈帧会被弹出,程序回到上一个栈帧的返回地址,继续执行剩下的代码。
第一个组合((()))的生成与回溯过程
以n=3为例,一步步拆解栈的变化:
- 初始调用
backtrack(0, 0, ""),压入栈帧A。 - 栈帧A中,
open_count <3成立,调用backtrack(1,0,"("),压入栈帧B。 - 栈帧B中,
open_count <3成立,调用backtrack(2,0,"(("),压入栈帧C。 - 栈帧C中,
open_count <3成立,调用backtrack(3,0,"((("),压入栈帧D。 - 栈帧D中,
open_count等于3,无法走左括号分支;但close_count(0) < open_count(3)成立,调用backtrack(3,1,"((()"),压入栈帧E。 - 栈帧E中,
open_count等于3,走右括号分支,调用backtrack(3,2,"((())"),压入栈帧F。 - 栈帧F中,
open_count等于3,走右括号分支,调用backtrack(3,3,"((()))"),压入栈帧G。 - 栈帧G中,
open_count == close_count ==3,把((()))加入res,然后return,栈帧G被弹出,回到栈帧F的返回地址(即调用backtrack(3,3,...)之后的位置)。 - 栈帧F没有剩余代码,直接
return,弹出栈帧F,回到栈帧E的返回地址。 - 栈帧E没有剩余代码,直接
return,弹出栈帧E,回到栈帧D的返回地址。 - 栈帧D没有剩余代码,直接
return,弹出栈帧D,回到栈帧C的返回地址——也就是执行完左括号分支后的位置。
此时栈帧C的代码还没执行完:它的第一个分支(调用backtrack(3,0,...))已经跑完,现在要执行第二个分支:判断close_count(0) < open_count(2),条件成立,于是调用backtrack(2,1,"(()")——这就是你调试时看到的close_count=1、open_count=2的场景。
为什么是这个场景?
递归的回溯是「原路返回」的:当最深处的栈帧执行完毕弹出后,程序会回到上一层未执行完的栈帧,继续处理该栈帧中剩下的分支逻辑。栈帧C的左括号分支已经走完,现在轮到处理它的右括号分支,自然就进入了open_count=2、close_count=1的调用。
附原代码
class Solution: def generateParenthesis(self, n: int): res = [] def backtrack(open_count, close_count, combination): if open_count == close_count == n: res.append(combination) return if open_count < n: backtrack(open_count + 1, close_count, combination + "(") if close_count < open_count: backtrack(open_count, close_count + 1, combination + ")") backtrack(0, 0, "") return res sol = Solution() result = sol.generateParenthesis(3)
内容的提问来源于stack exchange,提问作者Milad Sikaroudi
相关产品推荐
相关产品推荐

