zoukankan      html  css  js  c++  java
  • 22. Generate Parentheses

    ▶ 问题:给定正整数 n,求由 n 对小括号组成的所有合法表达式,显然所求表达式的个数为卡塔兰数 C(2n,n) / (n+1) 。

    ● 暴力枚举,超时。回溯法添加字符,添加够了以后进行检查,时间复杂度 O(22n)。

     1 class Solution
     2 {
     3 public:
     4     vector<string> output;
     5 
     6     inline bool valid(string input)
     7     {
     8         int i, count;
     9         for (i = count = 0; i < input.size(); i++)
    10         {
    11             (input[i] == '(') ? count++ : count--;
    12             if (count < 0)
    13                 return false;
    14         }
    15         return (count == 0);
    16     }
    17     void generate(string current, int pos, int length)
    18     {
    19         if (pos == length)
    20         {
    21             if (valid(current))
    22                 output.push_back(current);
    23         }
    24         else
    25         {
    26             current[pos] = '(';
    27             generate(current, pos + 1, length);
    28             current[pos] = ')';
    29             generate(current, pos + 1, length);
    30         }
    31     }
    32     vector<string> generateParenthesis(int n)
    33     {
    34         string current(2 * n, '');
    35         output = vector<string>();
    36         generate(current, 0, 2 * n);
    37         return output;
    38     }
    39 };

    ● 单边加项,3 ms,随时记录当前的 '(' 和 ')' 的数量,其中一个超过 n 的时候立即停止继续深入。前面穷举过程中大部分时间都浪费在了连左右括号数量都不相等的情况中。

     1 class Solution
     2 {
     3 public:
     4     
     5     void backtracking(vector<string>& ans, string s, int l, int r, int n)
     6     {
     7         if (l < n)                                  // 限制了添加 '('  和 ')' 的条件,注意添加过程是两条独立的分支
     8             backtracking(ans, s + "(", l + 1, r, n);
     9         
    10         if (r < l)
    11             backtracking(ans, s + ")", l, r + 1, n);
    12         else if (r == l && l == n)
    13             ans.push_back(s);
    14         return;
    15     }
    16     vector<string> generateParenthesis(int n)
    17     {
    18         vector<string> ans;
    19         backtracking(ans, "", 0, 0, n);
    20         return ans;
    21     }
    22 };

    ● 双边加项的递归,6 ms。每次从较短的合法表达式中取出两个,用 "(" + "左元" + ")" + "右元" 的范式加以组合。

     1 class Solution
     2 {
     3 public:
     4     vector<string> generateParenthesis(int n)
     5     {
     6         vector<string> output;
     7         if (n == 0)
     8         {
     9             output.push_back("");
    10             return output;
    11         }
    12         int i, j, k;
    13         string left, right;
    14         vector<string> leftVector, rightVector;
    15         for (i = 0; i < n; i++)
    16         {
    17             for (leftVector = generateParenthesis(i), j = 0; j < leftVector.size(); j++)
    18             {
    19                 left = leftVector[j];
    20                 for (rightVector = generateParenthesis(n - 1 - i), k = 0; k < rightVector.size(); k++)
    21                 {
    22                     right = rightVector[k];
    23                     output.push_back("(" + left + ")" + right);// 每次在左堆中取一个,右堆中取一个,结合成 "( 左元 ) 右元" 的形式
    24                 }
    25             }
    26         }        
    27         return output;
    28     }
    29 };

    ● 双边加项的非递归法,3 ms,免去了反复计算较短合法表达式的过程,但是空间开销大。注意到卡塔兰数的递推性质: an = an-1 a0 + an-2 a1 + ... + an-2 a1 + an-1 a0

     1 class Solution
     2 {
     3 public:
     4     vector<string> generateParenthesis(int n)
     5     {
     6         vector<vector<string>> table(n + 1);
     7         table[0] = vector<string>({ "" }); 
     8         int i, j, k, lp;
     9         for (i = 1; i <= n; i++)                                    // write all combinations of i pair of brackets  in row i of variable 'table'
    10         {
    11             for (lp = 0; lp < i; lp++)                              // size of the left part, from 0 to i-1
    12             {
    13                 for (j = 0; j < table[lp].size(); j++)              // take one element in row lp as left part           
    14                 {
    15                     for (k = 0; k < table[i - 1 - lp].size(); k++)  // take one element in row i-1-lp as right part
    16                         table[i].push_back("(" + table[lp][j] + ")" + table[i - 1 - lp][k]);
    17                 }                                                   // combinate the two parts like Approach #3
    18             }
    19         }
    20         return table[n];
    21     }
    22 };

     ● 大佬的回溯法,3 ms,类似单边加项法。

     1 class Solution
     2 {
     3 public:
     4     vector<string> generateParenthesis(int n)
     5     {
     6         vector<string> res;
     7         paren(n, 0, res, "");
     8         return res;
     9     }
    10     void paren(int n, int m, vector<string>& res, string str)// n 代表剩余 '(' 数量,m 代表当前 () 层数
    11     {
    12         if (m == 0 && n == 0)
    13         {
    14             res.push_back(str);
    15             return;
    16         }
    17         if (n > 0)
    18             paren(n - 1, m + 1, res, str + "(");// 尝试添加一个左括号
    19         if (m > 0)
    20             paren(n, m - 1, res, str + ")");    // 尝试添加一个右括号
    21     }
    22 };
  • 相关阅读:
    前端代码异常日志收集与监控
    基于window.onerror事件 建立前端错误日志
    MySQL数据类型和常用字段属性总结
    MySQL中char(36)被认为是GUID导致的BUG及解决方案
    dl,dt,dd,ul,li,ol区别
    泛型
    EF里Guid类型数据的自增长、时间戳和复杂类型的用法
    EF里的默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射
    EF里的继承映射关系TPH、TPT和TPC的讲解以及一些具体的例子
    SQL JOIN
  • 原文地址:https://www.cnblogs.com/cuancuancuanhao/p/8271410.html
Copyright © 2011-2022 走看看