1. 暴力解法
- 输入n个,就有n对括号,那么总共是2n个字符位置,
- 2n个位置 每个字符都可以是 ( 或者 )一共有2的2n次方个不同的字符串
- 判断所有的解法中有效的字符串即可
var generateParenthesis = function (n) {
if (n < 1) return [
let res = [];
// 判断一个组合是否有效
function valid(arr) {
let b = 0, i = 0;
for (; i < arr.length; i++) {
if (arr[i] === '(') {
b++;
} else {
b--;
}
// b < 0 代表前边多一个 ) 肯定没法闭合了
if (b < 0) return false;
}
return b === 0;
}
function generateAll(cur, pos) {
cur = [...cur]
if (pos === cur.length) {
if (valid(cur)) res.push(cur.join(""))
} else {
let c1 = [...cur]
c1[pos] = '('
generateAll(c1, pos + 1);
let c11 = [...cur]
c11[pos] = ')'
generateAll(c11, pos + 1);
}
}
generateAll(Array(n * 2), 0)
return res;
};
console.log(generateParenthesis(3));
2. 暴力解法优化 回朔法 + 剪枝
其实不用每个都判断有没有效,结果可以缓存 ( 记做 left
的数量 和 )记做 right
的数量
一旦right > left 直接return left大于n个也return,可以避免大量多余的运算 ,如果能提前分析出,走这一条路并不能得到想要的结果,可以跳过这个分支,这一步操作叫 剪枝
var generateParenthesis = function (n) {
if (n < 1) return []
let res = [];
function generateAll(cur, pos) {
if (cur.left > n || cur.right > cur.left) {
return;
}
if (pos === cur.length) {
res.push(cur)
} else {
let c1 = {...cur};
c1[pos] = '(';
c1.left++;
generateAll(c1, pos + 1);
let c2 = {...cur}
c2[pos] = ')';
c2.right++;
generateAll(c2, pos + 1);
}
}
generateAll({length: 2 * n, left : 0, right: 0}, 0);
return res.map(e => Array.from(e).join(""))
};
3. 动态规划 DP
这个就要明白 n+ 1 的解是 由n的解,( 和 a )b ,a和b位置放有效的结果形成的,并利用上一次的解
function generateParenthesis (n) {
let cache = [[""]];
function fn(n) {
if (cache[n]) return cache[n];
let r = [];
for (let a = 0; a < n; a++) {
fn(a).forEach(left => {
fn(n - a - 1).forEach(right => {
r.push("(" + left + ')' + right)
})
})
}
cache[n] = r;
return r;
}
return fn(n)
}