1: 回溯算法常用模版
// 主函数
main(
{
// 1: 定义返回的结果集合res
// 2: 定义回溯所需的组合combination,索引index
// 3: 调用回溯函数
backtrack(XXXX);
// 4: 结束 return
}
// 回溯函数
void backtrack(res, combination, index)
{
// 递归结束条件
if (满足题目要求的目标) {
res.push_back(combination)
return;
}
// 临时变量
new_idx = index;
new_combination = combination;
for (选择列表) { // 或者if
// 1: 做选择
//按需更新当前组合 new_combination
// 2:递归回溯
backtrack(res, new_commbination, new_index);
// 3: 撤销选择
// 恢复当前组合new_combination = combination
}
// 上述可能使用for遍历选择列表 或者使用 if更新
}
2: 回溯算法剪枝
由于回溯算法是枚举所有满足题意的组合,在递归的过程中如果不进行剪枝处理,就会出现重复的、多余的的组合。因此我们需要通过合理的剪枝处理来进行筛选
常见的剪枝方法:
(1) 选择列表进行剪枝
backtrack(para1, para2, start, ...) {
for(int i = start; i < size; i++) { // 从start开始
backtrack(para1, para2, i(或者i+1), …); // 下一次递归从自己所在i开始,不再回头看。 (若为i+1 表示不再选择自己)
}//
}
(2) sort排序,之后去重
sort(candidates.begin(), candidates.end());
在backtrack之中
for (int i = start; i < candidates.size(); i++) {
if ((i >= start + 1) && candidates[i] == candidates[i - 1])
continue;
}
(3) view数组,标记搜索过的元素为true
vector<bool> view(nums.size(), false);// 这里的nums为原始的选择列表,view数组大小一致,初始为false
for(int i = 0; i < nums.size(); i++) {
if (view[i] == true) continue;
view[i] = true; // 标记,索引i的元素选择了
// backtrack
view[i] = false; // 撤销标记
}
(4) if return 结束没有必要的递归
组合总和
https://leetcode.cn/problems/combination-sum/
https://leetcode.cn/problems/combination-sum-ii/submissions/
括号生存
https://leetcode.cn/problems/generate-parentheses/
电话号码的字母组合
https://leetcode.cn/problems/letter-combinations-of-a-phone-number/
全排列
https://leetcode.cn/problems/permutations/
组合
https://leetcode.cn/problems/combinations/
子集
https://leetcode.cn/problems/subsets/
子集II
https://leetcode.cn/problems/subsets-ii/
复原IP地址