给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 n = 3,生成结果为:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/generate-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方法一:暴力法
暴力法就是将所有可能的排列都列举出来,再判断是否符合条件。
该方法中会用到全排列next_permutation()函数,须包含<algorithm>头文件,一般先排序,使用方法如下:
#include <stdio.h>
#include <algorithm>
using namespace std;
int main(){
int n;
while(scanf("%d",&n)&&n){
int a[1000];
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
sort(a,a+n);
do{
for(int i=0;i<n;i++)
printf("%d ",a[i]);
printf("\n");
}while(next_permutation(a,a+n));
}
return 0;
}
最终代码如下:
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> res;
if (n <= 0) return res;
if (n == 1) {
res.push_back("()");
return res;
}
int i;
string s="";
for (i = 0; i < n; i++) {
s += "()";
}
sort(s.begin(), s.end());
do {
if (isvalid(s)) {
res.push_back(s);
}
} while (next_permutation(s.begin() + 1, s.end()));
return res;
}
bool isvalid(string s) {
int cnt = 0;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '(') {
cnt++;
}
else {
cnt--;
}
if (cnt < 0) return false;
}
if (cnt == 0) return true;
else return false;
}
};
方法二:回溯算法(DFS)
具体思路如下:left、right分别表示左括号和右括号可用的数量,若两者都为0,则该表达式合法,加入最后的解集,若使用的右括号多于左括号,则该表达式不合法。剩下的都是不完整的合法表达式,若还有可用的左括号,则dfs下一步,右括号同理。
一开始把res在dfs函数的参数中,push_back等操作都正常,但是输出为空,当把res改成全局变量后就没有问题了。
class Solution {
public:
vector<string> res;
void dfs(string curStr, int left, int right) {
if (left == 0 && right == 0) {
res.push_back(curStr);
return;
}
if (left > right) {
return;
}
if (left > 0) {
dfs(curStr + '(', left - 1, right);
}
if (right > 0) {
dfs(curStr + ')', left, right - 1);
}
}
vector<string> generateParenthesis(int n) {
if (n <= 0) return res;
if (n == 1) {
res.push_back("()");
return res;
}
dfs("", n, n);
return res;
}
};
方法三:BFS
在每一个字符的添加过程中都增加检查的步骤。使用了队列,每次添加都先判断一下合理性。每次循环都遍历了固定位数的所有可能性,如第一次遍历了1位,第二次栈中存储了2位所有的可能性,第三次栈中存储了3位所有的可能性..
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> res;
if (n <= 0) return res;
queue<string> que;
stack<char> store;
que.push("(");
int i;
while(!que.empty()) {
string tmp = que.front();
int leftcnt = 0;
for (i = 0; i < tmp.size(); i++) {
if (tmp[i] == '(') {
leftcnt++;
}
}
if (tmp.size() == n * 2) {
res.push_back(tmp);
que.pop();
}
else {
if (leftcnt < n) {
que.push(tmp + "(");
}
bool valid = true;
for (i = 0; i < tmp.size(); i++) {
if (tmp[i] == '(') {
store.push(tmp[i]);
}
if (tmp[i] == ')') {
if (store.empty()) {
valid = false;
}
else store.pop();
}
}
if(store.empty()) valid=false;
if (valid==true && !store.empty()) {
que.push(tmp + ")");
}
while(!store.empty()) {store.pop();}
que.pop();
}
}
return res;
}
};
方法四:动态规划(DP)
动态规划最核心的内容是:
dp[n]="("+dp[i]+")"+dp[n-i-1],i∈(0,1,...,n-1)
也就是说,n对括号的情况可以由许多小于n对括号的不同情况组合而成
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> res;
if (n <= 0) return res;
if (n == 1) {
res.push_back("()");
return res;
}
unordered_map<int, vector<string>> dp;
vector<string> dp0,dp1;
dp0.push_back("");
dp[0] = dp0;
dp1.push_back("()");
dp[1] = dp1;
int i, j;
for (int i = 2; i <= n; i++) {//每次循环形成dp[i]的内容
vector<string> cur;
for (int j = 0; j < i; j++) {//str1遍历所有长度
vector<string> str1 = dp[j];
vector<string> str2 = dp[i - 1 - j];
for (auto s1 : str1) {
for (auto s2 : str2) {
cur.push_back("(" + s1 + ")" + s2);
}
}
}
dp[i] = cur;
}
return dp[n];
}
};