带有效括号的递归问题
Recursion Question with Valid Parenthesis
我正在尝试理解这个生成所有有效括号的解决方案。
我不明白的部分是递归如何找到所有不同的组合。当我调试代码时,我会监视整数“left”和“right”。在 generateParenthesis 中输入 2,在用 1 个有效括号填充“ans”后,在“return ans”处,我看到变量“right”减少,使 if 语句“right < left”有效。当我从未专门将其设置为这样做时,变量“正确”如何减少?简单来说,递归如何能够找到所有有效括号的情况,而不仅仅是这种情况下的一种情况?
下面我将问题与在线 Python 调试器一起包含在内,以了解我在说什么。
https://onlinegdb.com/LMNYtq3iR
https://leetcode.com/problems/generate-parentheses/
def generateParenthesis(N):
ans = []
S = ''
left = 0
right = 0
ans = backtrack(N, S, left, right, ans)
def backtrack(N, S, left, right, ans):
print(left)
if len(S) == 2 * N:
ans.append(S)
if left < N:
backtrack(N, S+'(', left+1, right, ans)
if right < left:
backtrack(N, S+')', left, right+1, ans)
return ans
generateParenthesis(2)
变量权从不减少。您会看到早期递归执行的价值。如果在输出中包含当前递归级别,它可能会变得更清楚:
print(" "*len(S), S, left, right)
0 0
( 1 0
(( 2 0
(() 2 1
(()) 2 2
() 1 1 <-- left and right do not decrease, this is the call after `( 1 0` above
()( 2 1
()() 2 2
顺便提一句,您在这里使用的 return
不一致。 backtrack
函数根本不需要 return
,因为它向 ans
参数添加元素,而 generateParenthesis
根本不需要 return。我建议删除 ans
参数并将函数更改为适当的生成器函数:
def generateParenthesis(N):
return backtrack(N, S="", left=0, right=0)
def backtrack(N, S, left, right):
print(" "*len(S), S, left, right)
if len(S) == 2 * N:
yield S
if left < N:
yield from backtrack(N, S+'(', left+1, right)
if right < left:
yield from backtrack(N, S+')', left, right+1)
print(list(generateParenthesis(2)))
这与框架在 Python 中的工作方式有关。在程序的执行过程中,我们基本上从使用具有来自 generateParanthesis 的值的变量调用的回溯开始,然后由于 left 小于 N,我们得到一个对 backtrack 的递归调用,left 被迭代,程序沿着那条线继续,在递归调用之后。所以你打开了一堆新的框架,做了一堆不同的操作,在程序到达下一行之前,发现右边和左边在那个框架中是相等的,然后 return 就是答案。您正在看到类似的事情发生;本质上你进入了一个较晚的框架,然后一旦该框架关闭,return 到一个较早的框架,其中权利较少。权利从未减少,程序只是回到了权利较少的较早框架。会推荐 pythontutor.com 来可视化它。
我正在尝试理解这个生成所有有效括号的解决方案。
我不明白的部分是递归如何找到所有不同的组合。当我调试代码时,我会监视整数“left”和“right”。在 generateParenthesis 中输入 2,在用 1 个有效括号填充“ans”后,在“return ans”处,我看到变量“right”减少,使 if 语句“right < left”有效。当我从未专门将其设置为这样做时,变量“正确”如何减少?简单来说,递归如何能够找到所有有效括号的情况,而不仅仅是这种情况下的一种情况?
下面我将问题与在线 Python 调试器一起包含在内,以了解我在说什么。 https://onlinegdb.com/LMNYtq3iR
https://leetcode.com/problems/generate-parentheses/
def generateParenthesis(N):
ans = []
S = ''
left = 0
right = 0
ans = backtrack(N, S, left, right, ans)
def backtrack(N, S, left, right, ans):
print(left)
if len(S) == 2 * N:
ans.append(S)
if left < N:
backtrack(N, S+'(', left+1, right, ans)
if right < left:
backtrack(N, S+')', left, right+1, ans)
return ans
generateParenthesis(2)
变量权从不减少。您会看到早期递归执行的价值。如果在输出中包含当前递归级别,它可能会变得更清楚:
print(" "*len(S), S, left, right)
0 0
( 1 0
(( 2 0
(() 2 1
(()) 2 2
() 1 1 <-- left and right do not decrease, this is the call after `( 1 0` above
()( 2 1
()() 2 2
顺便提一句,您在这里使用的 return
不一致。 backtrack
函数根本不需要 return
,因为它向 ans
参数添加元素,而 generateParenthesis
根本不需要 return。我建议删除 ans
参数并将函数更改为适当的生成器函数:
def generateParenthesis(N):
return backtrack(N, S="", left=0, right=0)
def backtrack(N, S, left, right):
print(" "*len(S), S, left, right)
if len(S) == 2 * N:
yield S
if left < N:
yield from backtrack(N, S+'(', left+1, right)
if right < left:
yield from backtrack(N, S+')', left, right+1)
print(list(generateParenthesis(2)))
这与框架在 Python 中的工作方式有关。在程序的执行过程中,我们基本上从使用具有来自 generateParanthesis 的值的变量调用的回溯开始,然后由于 left 小于 N,我们得到一个对 backtrack 的递归调用,left 被迭代,程序沿着那条线继续,在递归调用之后。所以你打开了一堆新的框架,做了一堆不同的操作,在程序到达下一行之前,发现右边和左边在那个框架中是相等的,然后 return 就是答案。您正在看到类似的事情发生;本质上你进入了一个较晚的框架,然后一旦该框架关闭,return 到一个较早的框架,其中权利较少。权利从未减少,程序只是回到了权利较少的较早框架。会推荐 pythontutor.com 来可视化它。