LeetCode 22. 括号生成

思路

方法一:回溯 + 剪枝

生成所有2n个‘(’ 和“)”构成的序列,逐一判断生成的序列是否是有效的括号组合。

这里可以剪枝:在左括号/右括号的数量到达n的时候,将剩余的(2n - 左括号数-右括号数)数量 的右括号/左括号补到字符串末尾,然后对此字符串进行判断。

判断方法有两种:

(1)使用栈进行判断,遍历这个序列,遇到左括号就把其入栈,遇到右括号就把栈顶元素弹栈,如果栈为空则返回false

(2)使用变量进行判断,遍历这个序列,并使用一个变量 balance 表示左括号的数量减去右括号的数量。如果在遍历过程中 balance 的值小于零,或者结束时 balance 的值不为零,那么该序列就是无效的,否则它是有效的。

 1 class Solution {
 2 public:
 3     vector<string> generateParenthesis(int n) {
 4         vector<string> res;
 5         dfs(res, n, "", 0, 0);
 6         return res;
 7     }
 8 
 9     //使用变量判定
10     bool isValid(const string& s) {
11         int balance = 0;
12         for(const char& c: s) {
13             if(c == '(')
14                 balance++;
15             else
16                 balance--;
17             if(balance < 0)
18                 return false;
19         }
20 
21         return balance == 0;
22     }
23 
24 /*  //使用栈判定
25     bool isValid(const string& s) {
26         stack<char> mystack;
27         for(int i = 0; i < s.length(); ++i) {
28             if(s[i] == '(') {
29                 mystack.push(s[i]);
30             } else {
31                 if(mystack.empty())
32                     return false;
33                 else {
34                     mystack.pop();
35                 }
36             }
37         }
38 
39         return true;
40     }
41 */
42 
43     void dfs(vector<string>& res, int n, string s, int lBracketNum, int rBraketNum) {
44         if(lBracketNum == n) {          //剪枝
45             for(int i = lBracketNum+rBraketNum; i < 2*n; ++i) 
46                 s += ')';
47                 
48             if(isValid(s))
49                 res.push_back(s);
50             return;
51 
52         } else if(rBraketNum == n) {    //剪枝
53             for(int i = lBracketNum+rBraketNum; i < 2*n; ++i) 
54                 s += '(';
55 
56             if(isValid(s))
57                 res.push_back(s);
58             return;
59         }
60 
61         //分别深搜这两种情况
62         dfs(res, n, s+'(', lBracketNum+1, rBraketNum);
63         dfs(res, n, s+')', lBracketNum, rBraketNum+1);
64     }
65 };

方法二:回溯 + 剪枝

方法一中的剪枝还是不够好,还有一种更优化的剪枝:

我们可以只在序列仍然保持有效时才添加 '(' or ')',而不是像 方法一 那样每次添加。我们可以通过跟踪到目前为止放置的左括号和右括号的数目来做到这一点,

如果左括号数量不大于 n,我们可以放一个左括号。如果右括号数量小于左括号的数量,我们可以放一个右括号。

 1 class Solution {
 2 public:
 3     vector<string> generateParenthesis(int n) {
 4         vector<string> res;
 5         dfs(res, n, "", 0, 0);
 6         return res;
 7     }
 8 
 9     void dfs(vector<string>& res, int n, string s, int lBracketNum, int rBraketNum) {
10         if(s.length() == 2*n) {
11             res.push_back(s);
12             return;
13         }
14 
15         if(lBracketNum < n) 
16             dfs(res, n, s+'(', lBracketNum+1, rBraketNum);
17         
18         if(rBraketNum < lBracketNum) 
19             dfs(res, n, s+')', lBracketNum, rBraketNum+1);
20     }
21 };

复杂度分析

这种情况是一个典型的卡特兰数,如果不了解卡特兰数可以转向这里:卡特兰数及其应用

参考

力扣官方题解 - 括号生成

原文地址:https://www.cnblogs.com/FengZeng666/p/13920672.html