从零学算法22

22.数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3
输出:[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”]
示例 2:
输入:n = 1
输出:[“()”]

  • 观察会发现,有效的组合在生成时一定满足左括号多余等于右括号,否则比如 ()) 无论之后怎么加括号最后都是无效组合,生成的过程不难想到用回溯法,在每一位尝试放左括号或者右括号,在前一步的基础上继续各自递归尝试,很像二叉树遍历,基本上套用回溯模版即可
  • 回溯模板
  •   private void backtrack("原始参数") {
          //终止条件(递归必须要有终止条件)
          if ("终止条件") {
              //一些逻辑操作(可有可无,视情况而定)
              return;
          }
      
          for (int i = "for循环开始的参数"; i < "for循环结束的参数"; i++) {
              //一些逻辑操作(可有可无,视情况而定)
      
              //做出选择
      
              //递归
              backtrack("新的参数");
              //一些逻辑操作(可有可无,视情况而定)
      
              //撤销选择
          }
      }
    
  • 最终代码
  •   List<String> res = new ArrayList<>();
      public List<String> generateParenthesis(int n) {
          dfs(n, n, "");
          return res;
      }
      // left:能放的左括号数量,right 同理
      // str:当前生成的字符串
      public void dfs(int left, int right, String str){
      	// 说明此时右括号数量多余左括号,直接舍弃
          if(left>right)return;
          // 左右括号都用完就记录该组合
          if(left==0 && right == 0){
              res.add(str);
              return;
          }
          // 左括号用完就继续加右括号,以下同理
          if(left==0)dfs(left,right-1,str+")");
          else if(right==0)dfs(left-1,right,str+"(");
          else{
              dfs(left-1,right,str+"(");
              dfs(left,right-1,str+")");
          }
      }
    
  • 他人题解:思路一致,代码更精简。实际上只要左括号还有,并且左括号数量大于等于左括号数量,就说明 right>=left>=0,那就可以直接递归,所以可以不分情况
  •   public void dfs(int left, int right, String str){
      	// 左括号都用到负数了肯定不行了
      	// 右括号更多必定无效组合
      	// 并且如果通过了以下两个条件,就说明  right 也 >=0,所以不需要再判断 right
          if(left<0 || left>right)return;
          if(left==0 && right == 0){
              res.add(str);
              return;
          }
          dfs(left-1,right,str+"(");
          dfs(left,right-1,str+")");
      }
    
  • 他人题解2:动态规划,从感觉上来说是感觉可以 dp 的,但是这个公式真不太好想。
  • 设 dp[i] 表示 n 等于 i 时生成的有效括号组合,递推公式为:dp[i] = "(" + dp[m] + ")" + dp[k], m + k = i-1 。具体写的时候,这里比较绕的就是 dp[i] 是一个 list,比如 dp[1] = ["()"]dp[2]=["()()", "(())"] ,所以即使知道知道动态规划方程, dp 部分还是比较难理解
  • dp[i] = "(" + dp[m] + ")" + dp[k] 的理解:这里的意思其实是 dp[m] 中每个组合和 dp[k] 中每个组合,结合成新的结果。比如 "(" + dp[m] + ")" + dp[k] 先简化理解成 dp[m]+dp[k] ,假设 dp[m]=[1,2], dp[k]=[3,4] ,那么其实得到结果应该为 [13,14,23,24] 所以核心部分如下:
  •   for (int m = 0; m < i; m++) {
          int k = i - 1 - m;
          List<String> str1 = dp[m];
          List<String> str2 = dp[k];
          for (String s1 : str1) {
              for (String s2 : str2) {
                  cur.add("(" + s1 + ")" + s2);
              }
          }
      }
    
  • 边界条件为 dp[0]= "" ,所以最终代码如下
  •   public List<String> generateParenthesis(int n) {
      	// 初始化 [[""]],不然 dp[i-1] 都没东西
          List<String>[] dp = new List[n + 1];
          List<String> dp0 = new ArrayList<>();
          dp0.add("");
          dp[0] = dp0;
          
          for (int i = 1; i <= n; i++) {
          	// 记录 dp[i] 的组合
              List<String> cur = new ArrayList<>();
              for (int m = 0; m < i; m++) {
                  int k = i - 1 - m;
                  List<String> str1 = dp[m];
                  List<String> str2 = dp[k];
                  for (String s1 : str1) {
                      for (String s2 : str2) {
                          cur.add("(" + s1 + ")" + s2);
                      }
                  }
              }
              dp[i] = cur;
          }
          return dp[n];
      }
    
  • 他人题解2优化:上面 dp 的核心就在于 dp[m] 和 dp [k] 的组合,但是 dp[m] 其实不就是 generateParenthesis(m),所以可以优化如下
  •   public static List<String> generateParenthesis(int n) {
          List<String> list = new ArrayList<>();
          if (n == 0) {//边界条件的判断
              list.add("");
              return list;
          }
          for (int m = 0; m < n; m++) {
              int k = n - m - 1;
              List<String> first = generateParenthesis(m);
              List<String> second = generateParenthesis(k);
              for (String left : first) {
                  for (String right : second) {
                      list.add("(" + left + ")" + right);
                  }
              }
          }
          return list;
      }
    

你可能感兴趣的:(算法学习,#,回溯,算法,深度优先)