数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。
输入:n=3
输出:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
思路:
class Node {
public:
int num;
Node* right;
Node* left;
Node(int i) :num(i) {
right = left = nullptr;
}
};
class Tree {
public:
Node* head;
vector str;
Tree() {
head = new Node(1);
}
void add(Node* tem) {
if (tem->left!= nullptr&&tem->right!=nullptr) {//如果tem的孩子不空
add(tem->left);
add(tem->right);
}
else{
tem->left = new Node(1);
tem->right = new Node(-1);
}
}
void mida(Node* n,string str1,int sum) {//前提n不为空
if (n->num == -1) {
str1 += ')';
sum += -1;
}
else if (n->num == 1) {
str1 += '(';
sum += 1;
}
if (n->left != nullptr&&n->right!=nullptr) {//n不是叶节点
if (sum < 0) {//如果出现了不合理括号
return;
}
else {
mida(n->left, str1,sum);
mida(n->right, str1,sum);
}
}
else {//n是叶子节点
if (sum == 0) {//前括号与后括号数目相等
str.push_back(str1);
}
}
}
};
class Solution {
public:
vector generateParenthesis(int n) {
Tree a;
for (int i = 0; i < 2*n-1; i++) {
a.add(a.head);
}
a.mida(a.head, "", 0);
return a.str;
}
};
这是我第一遍做的方法,优点:思路清晰,简单易懂;
缺点:耗时太长,内存消耗大
还是来学习大神们的方法吧:
思路:朴实无华的递归。
(哭了,为啥我就想不到这样做呢QAQ)
class Solution {
public:
vector generateParenthesis(int n) {
vector res;
func(res, "", 0, 0, n);
return res;
}
void func(vector& res, string str, int l, int r, int n) {
if (l > n || r > n || r > l)
return;
if (l == n && r == n) {
res.push_back(str);
return;
}
func(res, str + '(', l + 1, r, n);
func(res, str + ')', l, r + 1, n);
return;
}
};
耗时和内存也十分优秀:
思路:运用二维vector,由0对括号和1对括号推出2对括号的组成,再由0对、1对、2对括号推出3对括号的组成……以此类推。
这个vector dp的结构是这样的:
dp[0] |
" " |
dp[1] | " () " |
dp[2] | " (())" , "()()" |
dp[3] | "((()))" , "(()())" , "()(())" , "()()()" , "(())()" |
class Solution {
public:
vector generateParenthesis(int n) {
if (n == 0) return {};
if (n == 1) return { "()" };
vector> dp(n + 1);
dp[0] = { "" };
dp[1] = { "()" };
for (int i = 2; i <= n; i++) {
for (int j = 0; j < i; j++) {
for (string p : dp[j])
for (string q : dp[i - j - 1]) {
string str = "(" + p + ")" + q;
dp[i].push_back(str);
}
}
}
return dp[n];
}
};
(这栈真是该死的甜美,叹服)
思路:将每一步的加括号结果都弄进栈,同时标记原来的括号序列。得到一个结果后,将该结果出栈,回到初始的序列。
举个栗子:
n=2,此时的栈top为"()",status=0:
class Solution {
public:
struct Data {
string str;
int lc;
int rc;
int status;
};
stack st;
vector generateParenthesis(int n) {
n *= 2;
vector res;
st.push(Data{ "", 0, 0, 0 });
while (st.empty() == false) {
Data& t = st.top();
if (t.str.size() == n) {
res.push_back(t.str);
st.pop();
continue;
}
if (t.status == 2) {
st.pop();
continue;
}
if (t.status == 0) {
if (t.rc < t.lc) {
st.push(Data{ t.str + ")", t.lc, t.rc + 1, 0 });
}
t.status = 1;
continue;
}
if (t.status == 1) {
if (t.lc - t.rc < n - t.str.size()) {
st.push(Data{ t.str + "(", t.lc + 1, t.rc, 0 });
}
t.status = 2;
continue;
}
}
return res;
}
};
参考了以上代码后,感到无比快乐的我又写了列表法
思路:与方法一类似,list str用来记录括号序列,list sign用来记录标记值的和。
class Solution {
public:
vector generateParenthesis(int n) {
n = n * 2;
vector a;
list str;
list sign;
str.push_back("(");
sign.push_back(1);
while (str.front().length() < n) {
if (sign.front() >= 0) {
string str1 = str.front() + "(";
str.push_back(str1);
sign.push_back(sign.front() + 1);
}
if (sign.front() > 0) {
string str2 = str.front() + ")";
str.push_back(str2);
sign.push_back(sign.front() - 1);
}
str.pop_front();
sign.pop_front();
}
while (str.empty() == false) {
if (sign.front() == 0) {
a.push_back(str.front());
}
sign.pop_front();
str.pop_front();
}
return a;
}
};
内存消耗有了很大进步,但是运行时间依旧不怎么样。
希望能早日熟练运用大神解法。
注:题目来自leetcode,部分代码来自评论区,感谢各位的分享。