LeetCode 22 题,括号生成(中等 )

91 阅读2分钟

题目描述

数字 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,那么我们可以枚举出所有的答案:

  1. "((()))"
  2. "(()())"
  3. "(())()"
  4. "()(())"
  5. "()()()" 可以看出如果n为3的话,我们一共就有6个格子可以用来放括号,而且需要满足几个条件
  • (递归的第一个分支)每一次放括号的时候,如果左边的括号没有放完,就可以放左边的括号(这句话其实很容易让人陷入一个思维误区,就是:如果一直放左边的括号,那么最终结果不是一定是 ((())))这样子的吗?但其实不是,把递归的树画出来之后我们就了解到,它的另外一个分支也会入栈做递归的,下面会有解释。
  • (递归的第二个分支)每一次放括号的时候,如果左边的括号数量大于右边括号的数量,我们才能放右边的括号,并且右边的括号数量也不能超过n
  • (递归的出口)如果左边的括号和右边的括号都使用了n个,那么这个时候就可以退出了。

递归树,更好理解

WechatIMG2.jpeg

剪枝

图片中我们可以看到,2,3 这个分支被我叉掉了,因为这个分支没有存在的必要,实际上我们的代码也不会跑到这一步的。
因为只有当左边的括号数量大于右边括号数量的时候,我们才会使用右边的括号,否则就不用右边的,一用就出错啦。
对应代码里面就是:

if (leftUsed > rightUsed && rightUsed < n)