灵神【基础算法精讲】视频的个人笔记。
视频例题
77.组合
从根节点往下
- 第一行是在集合中选一个数的情况
- 第二行是在集合中选两个数的情况
- 第三行是在集合中选三个数的情况
这就跟组合问题很像了。
设path长为m,那么还需要选d=k-m个数。 设当前需要从[1,i]这i个数中选数,如果i<d,最后必然无法选出k个数。 不需要继续递归,这是一种剪枝技巧
写法1:选和不选
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
int _k;
void dfs(int i) {
int d = _k - path.size(); //还要选a个数
if(d <= 0) {
ans.emplace_back(path);
return;
}
//选
path.emplace_back(i);
dfs(i - 1);
path.pop_back(); //恢复
//不选
if(i > d) //剩余的数 比 还要选的数 少, 不能不选
dfs(i - 1);
}
vector<vector<int>> combine(int n, int k) {
_k = k;
dfs(n);
return ans;
}
};
写法2:枚举下一个数选哪一个
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
int _k;
void dfs(int i) {
int d = _k - path.size(); //还要选a个数
if(d <= 0) {
ans.emplace_back(path);
return;
}
for (int j = i; j > 0; --j) {
path.emplace_back(j);
dfs(j - 1);
path.pop_back(); //恢复
}
}
vector<vector<int>> combine(int n, int k) {
_k = k;
dfs(n);
return ans;
}
};
216.组合总和 III
写法1:选和不选
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
int _k, _n;
void dfs(int i, int sum) {
if(sum == _n && path.size() == _k) { //符合条件
ans.emplace_back(path);
return;
}
if(path.size() > _k || i >= 10 || sum > _n) return; //base case
//选
path.emplace_back(i);
dfs(i + 1, sum + i);
path.pop_back(); //恢复
//不选
dfs(i + 1, sum);
}
vector<vector<int>> combinationSum3(int k, int n) {
_k = k; _n = n;
dfs(1, 0);
return ans;
}
};
写法2:枚举下一个数选哪一个
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
int _k, _n;
void dfs(int i, int sum) {
if(sum == _n && path.size() == _k) { //符合条件
ans.emplace_back(path);
return;
}
if(path.size() > _k || sum > _n) return; //base case
for (int j = i; j < 10; ++j) {
path.emplace_back(j);
dfs(j + 1, sum + j);
path.pop_back(); //恢复
}
}
vector<vector<int>> combinationSum3(int k, int n) {
_k = k; _n = n;
dfs(1, 0);
return ans;
}
};
22.括号生成
该题选左括号或者选右括号,两种选择,可以转换成选和不选问题。
写法1:选和不选
class Solution {
public:
vector<string> ans;
string path;
int _n;
void dfs(int i, int leftCnt){
if(i >= 2 * _n) {
ans.emplace_back(path);
return;
}
//选左括号
if(leftCnt < _n) { //左括号数小于n
path[i] = '(';
dfs(i + 1, leftCnt + 1);
}
//选右括号
if(i - leftCnt < leftCnt) { //右括号数小于左括号数
path[i] = ')';
dfs(i + 1, leftCnt);
}
}
vector<string> generateParenthesis(int n) {
string s(2 * n, 0);
path = s;
_n = n;
dfs(0, 0);
return ans;
}
};
力扣参考灵神题解
选哪一个
class Solution {
public:
vector<string> ans;
vector<int> path2;
int _n;
// balance = 左括号个数 - 右括号个数
void dfs(int i, int balance) {
if(path2.size() == _n) {
string s(_n * 2, ')');
for (int j: path2) s[j] = '(';
ans.emplace_back(s);
return;
}
// 可以填 0 到 balance 个右括号
for (int j = 0; j <= balance; ++j) { // 填 j 个右括号
path2.emplace_back(i + j); // 填 1 个左括号
dfs(i + j + 1, balance - j + 1);
path2.pop_back();
}
}
vector<string> generateParenthesis(int n) {
_n = n;
dfs(0, 0);
return ans;
}
};
课后作业
301.删除无效的括号
宫水三叶 参考题解
score判断左右括号是否合法len记录路径最大长度_max最大括号数,左右括号中的最小值
枚举子串,每个字符选或不选,字母就直接选。
class Solution {
public:
set<string> ans;
int n; //字符串长度
int _max; //最大括号数
int len; //最大路径子串的长度
string s;
/**
* @param i 位置
* @param score 得分: 左括号+1, 右括号-1
* @param path 路径
*/
void dfs(int i, int score, string path) {
if(score < 0 || score > _max) return; //得分小于0 或 左括号数大于最大括号数, 不符合要求
if(i == n) {
if(score == 0 && path.size() >= len) { //得分平衡 且 path大于等于最大长度
if(path.size() > len) { //大于等于最大长度, 重置
len = path.size();
ans.clear(); //清空
}
ans.insert(path);
}
return;
}
char c = s[i];
if(c != '(' && c != ')') //字母直接选
dfs(i + 1, score, path + c);
else {
dfs(i + 1, score + (c == '(' ? 1 : -1), path + c); //不删
dfs(i + 1, score, path); //删除
}
}
vector<string> removeInvalidParentheses(string _s) {
n = _s.size();
s = _s;
//最大括号数 是 左右括号的最小值
int l = 0, r = 0;
for(char c: s) {
if(c == '(') l++;
else if(c == ')') r++;
}
_max = min(l, r);
dfs(0, 0, "");
return vector<string>(ans.begin(), ans.end());
}
};
通过预处理,得到最后的「应该删除的左括号数量」和「应该删掉的右括号数量」,来直接得到最终的 len,多增加一层剪枝。
class Solution {
public:
set<string> ans;
int n; //字符串长度
int _max; //最大括号数
int len; //最大路径子串的长度
string s;
void dfs2(int i, int score, string path, int l, int r) {
if(l < 0 || r < 0 || score < 0 || score > _max) return; //删多了 或 得分小于0 或 左括号数大于最大括号数, 不符合要求
if(l == 0 && r == 0 && path.size() == len) ans.insert(path);
if(i == n) return;
char c = s[i];
if(c == '(') {
dfs2(i + 1, score + 1, path + c, l, r); //不删
dfs2(i + 1, score, path, l - 1, r); //删除
} else if(c == ')') {
dfs2(i + 1, score - 1, path + c, l, r); //不删
dfs2(i + 1, score, path, l, r - 1); //删除
} else {//字母直接选
dfs2(i + 1, score, path + c, l, r);
}
}
vector<string> removeInvalidParentheses(string _s) {
n = _s.size();
s = _s;
//最大括号数 是 左右括号的最小值
int l = 0, r = 0;
int removeL = 0, removeR = 0;
for(char c: s) {
if(c == '(') {
l++;
removeL++;
}
else if(c == ')') {
r++;
if(removeL != 0) removeL--;
else removeR++;
}
}
len = n - removeL - removeR;
_max = min(l, r);
dfs2(0, 0, "", removeL, removeR);
return vector<string>(ans.begin(), ans.end());
}
};