初级算法练习第十九节

73 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第33天,点击查看活动详情

1. 引言

接下来继续下一题开展对应leetcode 习题课中初级算法题目的练习,这个就当我的学习笔记了,大家一起交流,让我们一起学习变得更好吧! 官网地址:leetcode.cn/leetbook/re…

2. 题型

1.括号生成

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

示例1:

输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例2:
输入:n = 1
输出:["()"]

提示:

1 <= n <= 8

思路:
首先本题的题意告诉我们这其实就是不停选括号,要么选左括号,要么选右括号,不同的方案后,补齐括号的结束位置。 并有如下这些约束的:

只要有左括号(有剩,就可以选左括号(。 如果存在多个左括号(((((的时候这么选,都还不能判定为非法。
当剩下的)比(多时,才可以选),否则,)不能选,选了就非法。
因为:剩下的右括号)比左括号(少,即,使用的右括号)比左括号(多,不能成双成对。

描述节点的状态有:此时构建的字符串,和左右括号所剩下的数量。

借鉴网图:image

当我们回溯算法,抓住这三个重点解析

  1. 选择 每次最多两个选择,选左括号或者右括号,每次的“选择”会展开出一棵解的空间树。并且用 DFS 遍历这棵树,找出所有的解,这个过程叫回溯。

  2. 约束条件 首先什么情况下可以选左括号,什么情况下又可以选右括号。 所以我们需要利用约束做“剪枝”,也就是说去掉不会产生解的选项,剪去不会通往合法解的分支。成为“修剪”。 比如(),现在左右括号各剩一个,再选)就成了()),不能让这个错的选择成为选项(不落入递归): 所以我们需要使用如下判断:

    if (right > left) { // 右括号剩的比较多,才能选右括号
        dfs(str + ')', left, right - 1);
    }
    
  3. 目标 当我们构建出一个用尽 n 对括号的合法括号串。 就代表着当构建的长度达到 2*n,我们就可以结束递归(不用继续选了)。

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

解答:

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

  const dfs = (lRemain, rRemain, str) => { // lRemain, rRemain 代表左右括号所剩的数量,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;
};