一、题目
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
二、解法
思路:回溯法
具体思想:用一个回溯函数,记录当前已经得到的组合,首先判断是否达到边界情况,然后添加组合,继续递归求解,最后回退组合。
本题中,有几个关键的地方:
- 使用字符串的长度作为边界情况判断的记录
- 要满足有效的括号组合,需要满足以下情况:
- 左括号数不能超过n
- 右括号数不能超过n
- 右括号数不能超过左括号数
因为需要知道左括号数和右括号数,只需要一个 l e f t N u m leftNum leftNum 即可,右括号数可以用 l e n g t h − l e f t N u m length-leftNum length−leftNum 计算出来
所以回溯函数的原型是:
void backtrace(vector<string>& ans,int n,string& cur,int length,int leftNum);
其中ans是记录答案的数组,n是题目所给条件,cur是已经得到的组合,length是长度(其实用cur.length()也行),leftNum是cur中的左括号数量。
- 一开始需要判断cur是否满足有效括号条件,如不满足直接返回。
- 然后判断是否达到边界条件,如达到则直接加入答案数组,并返回。
- 然后根据情况,选择是否加入左括号或者右括号。当cur是空字符串时,只能加入左括号。当 l e f t N u m = = n leftNum==n leftNum==n 时,只能加入右括号。其余情况,既可以加入左括号,也可以加入右括号。(反正一开始都会判断是否满足有效括号)
class Solution {
public:
void backtrace(vector<string>& ans,int n,string& cur,int length,int leftNum){
if(leftNum>n||length-leftNum>n||length-leftNum>leftNum) return;
if(length==n*2){
ans.push_back(cur);
return;
}
if(cur==""){
cur+='(';
backtrace(ans,n,cur,length+1,leftNum+1);
cur.pop_back();
}else if(leftNum==n){
cur+=')';
backtrace(ans,n,cur,length+1,leftNum);
cur.pop_back();
}else{
cur+='(';
backtrace(ans,n,cur,length+1,leftNum+1);
cur.pop_back();
cur+=')';
backtrace(ans,n,cur,length+1,leftNum);
cur.pop_back();
}
}
vector<string> generateParenthesis(int n) {
vector<string> ans;
string cur="";
backtrace(ans,n,cur,0,0);
return ans;
}
};