1. 题目
读题
https://leetcode.cn/problems/generate-parentheses/
这道题的题目描述是这样的:
给定一个整数n,表示括号对的数量,写一个函数来生成所有可能的有效括号组合。¹
有效括号组合的意思是,每个左括号都有一个对应的右括号,并且括号之间的嵌套关系是正确的。
例如,n = 3时,有以下五种有效括号组合:
((()))
(()())
(())()
()(())
()()()
考查点
这道题的考查点主要有以下几个:
1. **深度优先搜索(dfs)**的思想和实现,如何用递归或栈来遍历所有可能的括号组合。
2. **剪枝**的技巧,如何在生成括号的过程中,判断当前括号是否有效,避免无效的搜索。
3. **字符串**的操作,如何用字符串拼接或StringBuilder来构造括号组合。
4. **回溯**的方法,如何在递归返回时,恢复之前的状态,继续搜索其他分支。
这道题是一道经典的dfs题目,也是一道常见的面试题。掌握了这道题的解法,可以帮助你理解和解决其他类似的问题,比如生成所有可能的IP地址、生成所有可能的电话号码字母组合等。
2. 解法
思路
代码的思路。
首先,我们定义一个列表ans来存放结果,一个字符串cur来表示当前生成的括号组合,两个整数left和right来表示已经使用的左括号和右括号的数量,一个整数n来表示括号对的数量。
然后,我们调用dfs方法,传入ans, cur, left, right, n这五个参数。dfs方法的作用是递归地生成所有可能的括号组合,并把有效的组合加入到ans列表中。
在dfs方法中,我们首先判断当前字符串cur的长度是否等于2n,如果是,说明已经生成了一个有效的括号组合,因为每个括号对都有一个左括号和一个右括号,所以总共有2n个字符。这时候,我们把cur加入到ans列表中,并结束递归。
如果当前字符串cur的长度不等于2n,说明还可以继续添加括号。这时候,我们有两种选择,要么添加左括号,要么添加右括号。但是,并不是所有的选择都是有效的。我们需要满足以下两个条件:
- 左括号的数量不能超过n,否则就会出现多余的左括号。
- 右括号的数量不能超过左括号的数量,否则就会出现不匹配的右括号。
所以,我们只有在满足这两个条件的情况下,才能继续添加括号。具体地,如果左括号的数量left小于n,我们就可以递归地调用dfs方法,把左括号加入到当前字符串cur中,并把left加一。如果右括号的数量right小于左括号的数量left,我们就可以递归地调用dfs方法,把右括号加入到当前字符串cur中,并把right加一。
通过这样的递归过程,我们就可以遍历所有可能的括号组合,并把有效的组合加入到ans列表中。最后,我们返回ans列表作为结果。
剪枝是一种削减搜索空间的方法,它可以在搜索过程中,提前判断某些分支是否有必要继续搜索,如果没有必要,就把这些分支舍弃,从而减少无效的搜索次数。剪枝可以
具体实现
class Solution { public List<String> generateParenthesis(int n) { List<String> ans = new ArrayList<>(); // 存放结果的列表 dfs(ans, "", 0, 0, n); // 调用dfs方法 return ans; // 返回结果 } public void dfs(List<String> ans, String cur, int left, int right, int n) { if (cur.length() == 2 * n) { // 如果当前字符串长度等于2n,说明已经生成了一个有效的括号组合 ans.add(cur); // 把当前字符串加入结果列表 return; // 结束递归 } if (left < n) { // 如果左括号的数量小于n,可以继续添加左括号 dfs(ans, cur + "(", left + 1, right, n); // 递归调用,把左括号加入当前字符串,左括号数量加一 } if (right < left) { // 如果右括号的数量小于左括号的数量,可以继续添加右括号 dfs(ans, cur + ")", left, right + 1, n); // 递归调用,把右括号加入当前字符串,右括号数量加一 } } }