Description
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例:
输入:n = 3
输出:[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/generate-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
动态规划
这题 DP 的思路比较难找,其实是这样的:
先建立一个数组 dp,假设 dp 中的每个下标存着「当前下标可以凑成的括号全部种类」。目前我们知道最小的状态,也就是只有一对括号的时候, dp[1] = ['()']
。
对于之后的每一次状态,都先假设「拿出一个括号」包在最外面(所以后面的代码里会出现很多 i - 1
,就是减去了已经使用的括号)。
然后分别去计算这个拿出来的括号「内部」分别使用 0 ~ n 时可以有几种排列方式,并且在这个括号的外部,用「剩余的括号对数」可以有几种排列方式,这几种方式的「笛卡尔积」就是结果。
对于 dp[2]
:
我们假设有一对新加入的括号 ()
包所有情况的最外面,因为我们默认用掉了 1 对括号包在最外面,那么此时还剩下的可使用的括号对数是 2 - 1 = 1
,也就是 1 对。
那么我们思考一下, 被这对新括号所包括的内部(新加入的括号我会用空格分隔,便于观察):
-
包住「1 对括号时候的所有排列」,也就是
( () )
,此时正好用光两对括号。 -
包住「0 对括号时候的所有排列」,也就是
( )
,此时还剩一对括号,那么去找 dp[1],也就是剩余 1 对括号时的全部排列,放在( )
的右边,也就拼成了( )()
。
此时得出,dp[2] = ['(())', '()()']
。
对于 dp[3]
:
-
包住「2 对括号时候的所有排列」,从刚刚算出的 dp[2] 中分别取出所有情况,此时拼成了
(dp[2]的所有情况)
,也就是( (()) ), ( ()() )
。 -
包住「1 对括号时的所有排列」,从 dp[1]取所有情况,此时拼成了
( () )
,此时还剩下 1 对括号,再取 dp[1] 放在这个结果的右边,( () )()
。 -
包住「0 对括号时候的所有排列」,此时拼成了
( )
,此时还剩下 2 对括号,再取 dp[2]的所有情况拼在右边,( )(()), ( )()()
。
以上所有情况,就是 dp[3]的结果,dp[3] = ['( (()) )', ' ( ()() )', '( () )()', '( )(())', ' ( )()()']
。
所以,此题的状态转移方程是 dp[i] = "(" + dp[j] + ")" + dp[i- j - 1] , j = 0, 1, ..., i - 1
。
let generateParenthesis = function (n) {
let dp = []
// 这里放一个空字符串,不然遍历的时候会跳过
// dp[0]就是取出一个空的字符串拼接到括号的内部去
dp[0] = ['']
dp[1] = ['()']
for (let i = 2; i <= n; i++) {
let res = []
for (let j = 0; j <= i - 1; j++) {
let inners = dp[j]
let outers = dp[i - 1 - j]
for (let inner of inners) {
for (let outer of outers) {
res.push(`(${inner})${outer}`)
}
}
}
dp[i] = res
}
return dp[n]
}
回溯法
利用回溯法,不断的朝着前一个结果的尾部追加左括号或者右括号,即可枚举出全部结果。
注意条件限制,由于我们是只往尾部添加括号,所以右括号的使用数量不能大于左括号,否则无法形成结果,比如())
这种就不可能在往尾部追加其他括号的情况下得到一个解。
利用变量 left
、right
分别记录剩余的左右括号数量,利用 prev
记录上一轮递归中已经形成的中间结果,当 left
和 right
都为 0,就得到一个结果,记录进 res
中。
/**
* @param {number} n
* @return {string[]}
*/
let generateParenthesis = function (n) {
let res = []
let helper = (left, right, prev) => {
if (left < 0 || right < 0 || right < left) {
return
}
if (left === 0 && right === 0) {
res.push(prev)
return
}
helper(left - 1, right, prev + '(')
helper(left, right - 1, prev + ')')
}
helper(n, n, '')
return res
}