回溯分为组合篇、排列篇、分割篇和子集篇。
回溯具有高度模板化,以下为模版代码:
组合问题分为以下3种:
剪枝版本:
优化版本:
优化版本:
![](https://83.229.122.34/wp-content/uploads/2024/07/模版.png)
- 1 数组中可选择的元素可以至多可以选择一次
- 1 模版题;
- 2 循环中的下一个迭代的起始索引+1
- 2 数组中可选择的元素可以重复无限制选取
- 1 在模版题的基础上;
- 2 函数入口多增加越界判断(否则会无限循环);
- 3 循环中的下一个迭代的起始索引不变。
- 3 数组中可选择的元素可以至多先选择一次,同时返回结果没有重复子数组。
- 1 在模版题目的基础上
- 2 先对数组进行排序,使数组中重复元素挨着
- 3 增加used数组进行去重,nums[i-1] = num[i]则说明重复(该重复为同层之间的重复,同一个树枝中允许重复)
-
// used[i - 1] == true,说明同一树枝candidates[i - 1]使用过 // used[i - 1] == false,说明同一树层candidates[i - 1]使用过
1 组合问题
题目类型一。 给定两个整数n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。
你可以按 任何顺序 返回答案。
![](https://83.229.122.34/wp-content/uploads/2024/07/组合问题.png)
- k – path.size(): 还需要加入的节点数量
- n – (k – path.size()) + 1 : 下标需要在索引左边才能加入足够多的节点数量
![](https://83.229.122.34/wp-content/uploads/2024/07/组合问题-优化.png)
2 组合总和
题目类型一。 给你一个 无重复元素 的整数数组candidates
和一个目标整数 target
,找出 candidates
中可以使数字和为目标数 target
的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates
中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target
的不同组合数少于 150
个。
1 模版题; 2 循环中的下一个迭代的起始索引+1
![](https://83.229.122.34/wp-content/uploads/2024/07/组合总和.png)
- 1 candidates数组需要排序
- 2 for循环中total+candidates <= target
![](https://83.229.122.34/wp-content/uploads/2024/07/组合总和-优化.png)
3 组合总和III
题目类型二。 找出所有相加之和为n
的 k
个数的组合,且满足下列条件:
- 只使用数字1到9
- 每个数字 最多使用一次
1 在模版题的基础上; 2 函数入口多增加越界判断(否则会无限循环); 3 循环中的下一个迭代的起始索引不变。
![](https://83.229.122.34/wp-content/uploads/2024/07/组合总和III.png)
- 1 数组需要排序(已经有序)
- 2 for循环中total+candidates <= target
![](https://83.229.122.34/wp-content/uploads/2024/07/组合总和III-优化.png)
4 组合总和II
题目类型三。 给定一个候选人编号的集合candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
直接给出优化版本。
- 1 在模版题目的基础上
- 2 先对数组进行排序,使数组中重复元素挨着
- 3 增加used数组进行去重,nums[i-1] = num[i]则说明重复(该重复为同层之间的重复,同一个树枝中允许重复)
// used[i - 1] == true,说明同一树枝candidates[i - 1]使用过 // used[i - 1] == false,说明同一树层candidates[i - 1]使用过
![](https://83.229.122.34/wp-content/uploads/2024/07/组合总和II.png)
5 电话号码的字母组合
给定一个仅包含数字2-9
的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:
输入:digits = "23" 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]解题思路:
- 1 递归入口控制组合的长度;
- 2 for循环处控制每次组合可选择的数组范围;
class Solution {
public:
vector<string> result;
vector<string> vocabs = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
void traversal(string digits, string path){
// 控制组合长度
if (path.size() == digits.size()){
result.push_back(path);
return ;
}
int digit = digits[path.size()] - '2';
string letters = vocabs[digit];
// 控制选择来源
for(int i=0; i<letters.size(); i++){
traversal(digits, path+letters[i]);
}
}
vector<string> letterCombinations(string digits) {
if (digits.size() == 0) return {};
traversal(digits, "");
return result;
}
};