/** * @param {number} n * @return {string[]} */ const generateParenthesis = function (n) { }; /* 这个题目也是一个典型的回溯算法题,一个一个的尝试就行了,问题在于如何检测有效括号,这里可以定义一个栈,这里可以参考 leetcode20题。 思路:每一次backtrack从头到尾尝试所有括号,尝试所有的排列可能,如果长度达到2*n检测是否符合要求,如果符合要求, 就收集结果,最后返回结果集 */ function f1(n) { const result = []; // 结果集 // 初始化括号数组 const brackets = []; for (let i = 0; i < n; i++) { brackets.push('('); brackets.push(')'); } const bLen = backtrack.length; // 定义used数组,防止重复使用 const used = Array(bLen).fill(false); const backtrack = (path) => { // 如果path的长度等于brackets.length,并且有效括号数位n,则收集结果 if (path.length === bLen && checkBrackets(path)) { result.push(path.join('')); return; } for (let i = 0; i < bLen; i++) { if (used[i]) continue; path.push(brackets[i]); used[i] = true; backtrack(i + 1, path); path.pop(); used[i] = false; } }; backtrack([]); return Array.from(new Set(result)); } /* 检测字符串中的括号是否符合要求 */ function checkBrackets(backets) { let count = 0; for (const char of backets) { if (char === '(') { count++; } else if (char === ')') { count--; } // 如果右括号多于左括号,提前返回 false if (count < 0) return false; } // 所有括号遍历完后,必须完全闭合 return count === 0; } /* 上面的思路会超时,通过全排列去找符合要求的再检测,会指数爆炸,当n>=5时就已经跑不动了,正确的做法应该是 递归的构造符合要求的字符串,当构造的字符串符合要求时,再收集结果。 */ function f2(n) { const result = []; // 结果集 /** * * @param {String} path 收集的括号组合 * @param {Number} left 左括号的数量 * @param {Number} right 右括号的数量 */ const backtrack = (path, left, right) => { // 如果长度达到2*n就收集结果 if (path.length === 2 * n) { result.push(path); return; } // 如果左括号未达到n就继续构造左括号 if (left < n) { backtrack(`${path}(`, left + 1, right); } // 如果right >= left 再添加右括号会导致,缺少一个左括号和它闭合,所以只有当 right < left 时才添加右括号 if (right < left) { backtrack(`${path})`, left, right + 1); } }; backtrack('', 0, 0); return result; }