回溯法
回溯法:模板、套路、简化写法 - 子集 II - 力扣(LeetCode) (leetcode-cn.com)
回溯法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就「回溯」返回,尝试别的路径
回溯法是一种算法思想,而递归是一种编程方法,回溯法可以用递归来实现。
回溯法的整体思路是:搜索每一条路,每次回溯是对具体的一条路径而言的。对当前搜索路径下的的未探索区域进行搜索,则可能有两种情况:
-
当前未搜索区域满足结束条件,则保存当前路径并退出当前搜索;
-
当前未搜索区域需要继续搜索,则遍历当前所有可能的选择:如果该选择符合要求,则把当前选择加入当前的搜索路径中,并继续搜索新的未探索区域。
上面说的未搜索区域是指搜索某条路径时的未搜索区域,并不是全局的未搜索区域。
模板
res = []
path = []
def backtrack(未探索区域, res, path):
if path 满足条件:
res.add(path) # 深度拷贝
# return # 如果不用继续搜索需要 return
for 选择 in 未探索区域当前可能的选择:
if 当前选择符合要求:
path.add(当前选择)
backtrack(新的未探索区域, res, path)
path.pop()
求不含重复元素的数组的子集
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res;
vector<int> path;
helper(nums, res, path, 0);
return res;
}
void helper(const vector<int>& nums, vector<vector<int>>& res, vector<int>& path, int start) {
res.push_back(path);
for (int i = start; i < nums.size(); i ++) {
path.push_back(nums[i]);
helper(nums, res, path, i + 1);
path.pop_back();
}
}
};
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res;
vector<int> path;
res.push_back(path);
for(int i=0; i<nums.size(); i++){
int size = res.size();
for(int j=0; j<size; j++){
path = res[j];
path.push_back(nums[i]);
res.push_back(path);
}
}
return res;
}
};
求含重复元素的数组的子集
class Solution {
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int>> res;
vector<int> path;
backtrack(res, path, nums, 0);
return res;
}
void backtrack(vector<vector<int>>& res, vector<int>& path, vector<int>& nums, int index) {
if (index > nums.size()) return;
res.push_back(path);
for (int i = index; i < nums.size(); ++i) {
if (i != index && nums[i] == nums[i - 1]) continue;
path.push_back(nums[i]);
backtrack(res, path, nums, i + 1);
path.pop_back();
}
}
};