数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:
输入:n = 1
输出:["()"]
提示:
1 <= n <= 8
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/generate-parentheses
思路:因为数据量比较小,而且做过判断括号是否合法的题,所以最先想到的就是生成括号序列后,判断是否合法。
class Solution {
private:
//存储最终结果
vector<string> res;
//判断括号是否合法
bool isVaild(string s){
stack<char> st;
for(int i = 0;i<s.length();i++){
char c = s[i];
if(c=='('){
st.push(c);
}else{
if(st.empty()){
return false;
}else{
if(st.top()=='('){
st.pop();
}
}
}
}
if(st.empty())return true;
return false;
}
void myslove(int depth,string s,int N){
if(depth==N){
//合法
if(isVaild(s)){
res.push_back(s);
}
return;
}
//枚举当前位置的括号情况
myslove(depth+1,s+"(",N);
myslove(depth+1,s+")",N);
}
public:
vector<string> generateParenthesis(int n) {
//2*n个位置
myslove(0,"",2*n);
return res;
}
};
结果发现,好慢,而且内存消耗大。
思路:然后思考,那么是不是可以有些情况不用去搜索呢(剪枝)?然后发现,左括号和右括号数量是一定的,那么,当左括号或右括号大于n时,就不用再继续搜下去了(剪枝1)。同时,当剩余的左括号数大于右括号数时,也一定不合法(剪枝2)。
class Solution {
private:
vector<string> res;
//判断是否合法。
bool isVaild(string s){
stack<char> st;
for(int i = 0;i<s.length();i++){
char c = s[i];
if(c=='('){
st.push(c);
}else{
if(st.empty()){
return false;
}else{
if(st.top()=='('){
st.pop();
}
}
}
}
if(st.empty())return true;
return false;
}
void myslove(int depth,string s,int N){
if(depth==N){
if(isVaild(s)){
res.push_back(s);
}
return;
}
int left = 0;
int right = 0;
//统计左括号、右括号数量
for(int i = 0;i<s.length();i++){
if(s[i]=='('){
left++;
}else{
right++;
}
}
//左右括号数量进行剪枝
if(left>N/2)return;
if(right>N/2)return;
//根据剩余括号数量进行剪枝
if(left<right)return;
//当前位置可能情况
myslove(depth+1,s+"(",N);
myslove(depth+1,s+")",N);
}
public:
vector<string> generateParenthesis(int n) {
myslove(0,"",2*n);
return res;
}
};
思路:以上思路都是对每个位置的括号情况进行枚举。既然左括号和右括号数是一定的,我们可不可以尝试对左右剩余括号的数量进行枚举呢?当左括号或者右括号都小于0时,得到的最终序列肯定不合法,当剩余的左括号大于右括号时,肯定也不合法。除了以上情况外,当左括号为0并且右括号为0时,得到的一定是合法序列。
class Solution {
private:
vector<string> str;
void dfs(int left,int right,string temp){
//剩余左括号或者剩余右括号小于0,一定不合法。
if(left<0)return;
if(right<0)return;
//当剩余的左右括号都为0时,得到合法序列。
if(left==0&&right==0){
str.push_back(temp);
return;
}
//当剩余左括号大于剩余右括号时,一定不合法。
if(left>right){
return;
}
if(left>0){
dfs(left-1,right,temp+"(");
}
if(right>0){
dfs(left,right-1,temp+")");
}
}
public:
vector<string> generateParenthesis(int n) {
int l = n;
int r = n;
dfs(l,r,"");
return str;
}
};
为什么当剩余左括号和剩余右括号的数量都为0时,得到的一定是合法的序列呢?这是因为当一个括号序列不合法时,一定从某个位置开始,导致了剩余的左括号数大于了剩余右括号数。所以当剩余的左括号数大于了剩余右括号数这种情况排除了后,并且左右括号数一定时,当剩余左括号和剩余右括号的数量都为0时,一定时合法的。