一、回溯算法模板
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```cppif (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;}};
那么重点在于如何去重?
答:需要对数组排序,增加一个used数组