题目描述
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入: n = 3
输出: ["((()))","(()())","(())()","()(())","()()()"]
答案
/**
* @param {number} n
* @return {string[]}
*/
var generateParenthesis = function (n) {
const res = [];
// left代表左边括号的使用量,不能超过n
// right代表右边括号的使用量,1. 不能超过左括号的使用量 2. 不能超过n
// 这一题的递归有两个条件,所以不是单层递归下去的,可以考虑画出一个递归的树,这样能够清晰很多
const _gen = (leftUsed, rightUsed, n, str) => {
if (leftUsed === n && rightUsed === n) {
res.push(str);
return;
}
if (leftUsed < n) {
_gen(leftUsed + 1, rightUsed, n, str + "(");
}
if (leftUsed > rightUsed && rightUsed < n) {
_gen(leftUsed, rightUsed + 1, n, str + ")");
}
}
_gen(0, 0, n, "");
return res;
};
解题思路
首先可以假设n为3,那么我们可以枚举出所有的答案:
- "((()))"
- "(()())"
- "(())()"
- "()(())"
- "()()()" 可以看出如果n为3的话,我们一共就有6个格子可以用来放括号,而且需要满足几个条件
- (递归的第一个分支)每一次放括号的时候,如果左边的括号没有放完,就可以放左边的括号(这句话其实很容易让人陷入一个思维误区,就是:如果一直放左边的括号,那么最终结果不是一定是 ((())))这样子的吗?但其实不是,把递归的树画出来之后我们就了解到,它的另外一个分支也会入栈做递归的,下面会有解释。
- (递归的第二个分支)每一次放括号的时候,如果左边的括号数量大于右边括号的数量,我们才能放右边的括号,并且右边的括号数量也不能超过n
- (递归的出口)如果左边的括号和右边的括号都使用了n个,那么这个时候就可以退出了。
递归树,更好理解
剪枝
图片中我们可以看到,2,3 这个分支被我叉掉了,因为这个分支没有存在的必要,实际上我们的代码也不会跑到这一步的。
因为只有当左边的括号数量大于右边括号数量的时候,我们才会使用右边的括号,否则就不用右边的,一用就出错啦。
对应代码里面就是:
if (leftUsed > rightUsed && rightUsed < n)