「前端刷题」22. 括号生成

1,150 阅读1分钟

这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战

题目

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

有效括号组合需满足:左括号必须以正确的顺序闭合。

 

示例 1:

输入: n = 3

输出: ["((()))","(()())","(())()","()(())","()()()"]

示例 2:

输入: n = 1

输出: ["()"]

 

提示:

  • 1 <= n <= 8

解题思路

思路1

就是不停选括号啊~,要么选左括号,要么选右括号。

并且,是有约束的:

  • 只要(有剩,就可以选((((((这么选,都还不能判定为非法。
  • 当剩下的)(多时,才可以选),否则,)不能选,选了就非法了(见下图)。

描述节点的状态有:当前构建的字符串,和左右括号所剩的数量。
image.png

回溯,死抓三个要点

  • 选择
    • 在这里,每次最多两个选择,选左括号,或右括号,“选择”会展开出一棵解的空间树。
    • 用 DFS 的方式遍历这棵树,找出所有的解,这个过程叫回溯。
  • 约束条件
    • 即,什么情况下可以选左括号,什么情况下可以选右括号。
    • 利用约束做“剪枝”,即,去掉不会产生解的选项,即,剪去不会通往合法解的分支。
      • 比如(),现在左右括号各剩一个,再选)就成了()),这是错的选择,不能让它成为选项(不落入递归):

        if (right > left) { // 右括号剩的比较多,才能选右括号 dfs(str + ')', left, right - 1); }

  • 目标
    • 构建出一个用尽 n 对括号的合法括号串。
    • 意味着,当构建的长度达到 2*n,就可以结束递归(不用继续选了)。

充分剪枝的好处

经过充分的剪枝,所有不会通往合法解的选项,都被干掉,只要往下递归,就都通向合法解。
即,只要递归到:当构建的字符串的长度为 2*n 时,一个合法解就生成了,加入解集即可。

代码

var generateParenthesis = function (n) {
  const res = [];

  const dfs = (lRemain, rRemain, str) => { // 左右括号所剩的数量,str是当前构建的字符串
    if (str.length == 2 * n) { // 字符串构建完成
      res.push(str);           // 加入解集
      return;                  // 结束当前递归分支
    }
    if (lRemain > 0) {         // 只要左括号有剩,就可以选它,然后继续做选择(递归)
      dfs(lRemain - 1, rRemain, str + "(");
    }
    if (lRemain < rRemain) {   // 右括号比左括号剩的多,才能选右括号
      dfs(lRemain, rRemain - 1, str + ")"); // 然后继续做选择(递归)
    }
  };

  dfs(n, n, ""); // 递归的入口,剩余数量都是n,初始字符串是空串
  return res;
};

思路2

在学习回溯算法之前,你最好对树的 DFS 熟悉,因为回溯的问题基本都可以抽象成树形结构问题

你之所以觉得回溯难,是因为你的树形结构及其算法不熟悉

如果树的 DFS 不熟悉的话,或者想全面系统的学习回溯、贪心和动态规划的话,请看这里:构建数据结构与算法的知识体系

将问题抽象成树形结构遍历问题

代码:

`var generateParenthesis = function(n) { const res = [] if (n <= 0) return res

const dfs = (path, open, close) => {
    if (open > n || close > open) return
    if (path.length == 2 * n) {
        res.push(path)
        return
    }
    dfs(path + "(", open + 1, close)
    dfs(path + ")", open, close + 1)
}

dfs("", 0, 0)
return res

};`

最后

曾梦想仗剑走天涯

看一看世界的繁华

年少的心总有些轻狂

终究不过是个普通人

无怨无悔我走我路

「前端刷题」No.22