一、回溯算法模板
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
三部曲步骤:
- backtracking函数的参数和返回值
- 递归的终止条件
- 单层的处理逻辑
二、重点题目总结
2.1 77. 组合
该题目是第一道入门的回溯题目 ```cpp class Solution { private: vector> result; // 存放符合条件结果的集合 vector path; // 用来存放符合条件结果 void backtracking(int n, int k, int startIndex) {
} public: vectorif (path.size() == k) {
result.push_back(path);
return;
}
for (int i = startIndex; i <= n; i++) {
path.push_back(i); // 处理节点
backtracking(n, k, i + 1); // 递归
path.pop_back(); // 回溯,撤销处理的节点
}
> combine(int n, int k) {
} };result.clear(); // 可以不写
path.clear(); // 可以不写
backtracking(n, k, 1);
return result;
重点关注startIndex和递归终止条件的处理
该题目可以剪枝:i <= n - (k - path.size()) + 1
<a name="mB0fL"></a>
#### 2.2 216.组合总和III
> 该题目增加了一个限制条件,相加之和为n 且 长度为k
```cpp
if (path.size() == k) {
if (sum == targetSum) result.push_back(path);
return; // 如果path.size() == k 但sum != targetSum 直接返回
}
// 增加一个sum参数记录目前path中的总和。
// 当然可以每次用target - i,终止条件判断target是否为0。
sum += i;
path.push_back(i);
// 这里的两个if条件句不能用&&合并,仔细体会,不然递归函数可能无法终止
2.3 17.电话号码的字母组合
这道题目主要复杂在于:待选集合有多个
// 设置一个数组记录
const string keyMap[10] = {" ", " ", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
// 其他部分大差不差
//输入参数i来获取按键的位置索引,再根据i获取数组中的index,根据index去取字母
int index = digits[i] - '0';
2.4 39. 组合总和
candidates 中的数字可以无限制重复被选取,那么把”终止条件“和startIndex修改一下
// 把sum作为终止条件
if (sum > target) return;
else if (sum == target) {
ans.push_back(path);
return;
}
for (int i = startIndex; i < candidates.size(); i++) {
...
backtracking(candidates, target, sum, i); // 下一个递归的起始位置跟当前一样,也就是说当前元素可以重复
...
}
2.5 40. 组合总和 II
candidates中有重复元素,因此按照之前的做法会出现重复的结果。如下 那么重点在于如何去重? 答:需要对数组排序,增加一个used数组
class Solution {
private:
vector<int> path;
vector<vector<int>> ans;
void backtracking(vector<int>& candidates, int target, int sum, int startIndex, vector<bool>& used) {
if (sum > target) return;
else if (sum == target) {
ans.push_back(path);
return;
}
for (int i = startIndex; i < candidates.size(); i++) {
// 下面的if条件判断用来去重,“当有重复元素且前一个没有使用时,说明这种情况之前已经遍历过了”
if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) {
continue;
}
path.push_back(candidates[i]);
sum += candidates[i];
used[i] = true; // 设置是否使用标志位
backtracking(candidates, target, sum, i + 1, used);
path.pop_back();
sum -= candidates[i];
used[i] = false;
}
}
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<bool> used(candidates.size(), false); // 排序
sort(candidates.begin(), candidates.end());
backtracking(candidates, target, 0, 0, used);
return ans;
}
};