题目

知识点
function定义的递归函数
CSDN详细解释链接
B站视频讲解【C++ function使用说明】
https://player.bilibili.com/player.html?bvid=BV1XaaEe1Epz&autoplay=0
emplace_back()
知乎详细解释链接
在 vector 容器的尾部添加一个元素。
思路------K神
选与不选
三种情况:
- 右括号的个数等于最大值
- 左括号的个数小于最大值
- 右括号的个数小于左括号的个数
本质是「选或不选」的思想,你可以把填左括号视作「选」,填右括号视作「不选」。
递归的过程中,要保证右括号的个数不能超过左括号的个数。
如果现在右括号个数等于左括号个数,那么不能填右括号。
如果现在右括号个数小于左括号个数,那么可以填右括号。
由于左括号个数始终 ≥ 右括号个数,且至多填 n 个左括号,所以当我们填了 n 个右括号时,也一定填了 n 个左括号,此时填完所有 2n 个括号。
答疑
问:什么时候需要写恢复现场,什么时候不需要写?
答:下面代码中,如果初始化 path 为空列表,就需要写恢复现场。本题由于所有括号长度都是固定的 2n,我们可以创建一个长为 2n 的 path 列表,在递归时直接写入字符(而不是插入字符),这样做无需写恢复现场。
枚举(难理解 但必须掌握)
用「枚举选哪个」的思路。在从左往右填的过程中,要时刻保证右括号的个数不能超过左括号的个数。
如果前面填了 5 个左括号,2 个右括号,那么还能填几个右括号?至多填 5−2=3 个。
所以枚举(在填下一个左括号之前)填入了 0,1,2,3 个右括号,这样就能得到下一个左括号的位置。
为了方便,代码直接用 balance 表示左右括号之差。这样我们枚举的范围就是 [0 , balance]。
注意最后一个左括号的右边还可以填右括号,但无需考虑。填入所有左括号后,剩余的位置我们会自动填入右括号。
不直接构建括号字符串,而是记录所有左括号的位置。
错误
function()函数格式
function<void(int,int)> dfs = [&](int left,int right) {};
right<=balance
right <= balance 中的等于号允许我们在放置下一个左括号之前,匹配掉所有当前未匹配的左括号。这是生成像 ()() 这样括号对完全独立(非嵌套)的组合所必需的。
如果不允许等于号(即 right < balance),那么每次放下一个左括号前必须至少保留一个未匹配的左括号,这样就会强制所有括号对必须嵌套,无法生成并列的括号对。
题解
选与不选
cpp
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> ans;//存放最终结果 括号组成的所有形式
string path(n * 2, 0);//长度为2n的字符串,初始值都为0
//使用function定义的递归函数,使用std::function
//void(int, int)函数接受两个int参数,返回值为void
function<void(int, int)> dfs = [&](int left, int right) {//以引用的方式获取所有外部变量:ans/path/n
//当右括号数量达到n时,说明已经填满了2n个位置
if (right == n) {
ans.emplace_back(path);//在 vector 容器的尾部添加一个元素
return;
}
//左括号还可以添加(还没达到n个)
if (left < n) {
path[left + right] = '(';//left+right 是已经填写的括号总数,也就是下一个要填的位置
dfs(left + 1, right);//左括号数量+1
}
//右括号数量小于左括号数量
if (right < left) {
path[left + right] = ')';
dfs(left, right + 1);
}
};
dfs(0, 0);
return ans;
}
};
枚举
cpp
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> ans;
vector<int> path;//记录左括号们的下标
//i当前要填写的位置下标,balance = 已填的左括号数 - 已填的右括号数
function<void(int, int)> dfs = [&](int i, int balance) {
if (path.size() == n) {
string s(n * 2, ')');//创建一个长度为2n,元素全是右括号的字符串s
for (int j : path) {
s[j] = '(';
}//把下标为path的位置都改成左括号
ans.emplace_back(s);//存入ans中
return;
}
for (int right = 0; right <= balance; right++) {
path.push_back(i + right);
//dfs(当前位置加上right个右括号和1个左括号,balance去掉right个右括号加上1个左括号)
dfs(i + right + 1, balance - right + 1);
path.pop_back();//回溯
}
};
dfs(0, 0);
return ans;
}
};