78.子集
class Solution {
private:
vector<int> path;
vector<vector<int>> result;
void backtracking(vector<int>& nums,int startIndex){
result.push_back(path);
if(startIndex==nums.size()) return;
for(int i=startIndex;i<nums.size();i++){
path.push_back(nums[i]);
backtracking(nums,i+1);
path.pop_back();
}
return;
}
public:
vector<vector<int>> subsets(vector<int>& nums) {
path.clear();
result.clear();
backtracking(nums,0);
return result;
}
};
这道题和之前的组合切割不同,它需要遍历树形结构的每一个节点,递归的终止条件也可以省略。
递归的参数为传入的数组,遍历的起始下标startIndex;终止条件为当startIndex等于数组长度时return,因为本题是要遍历数的每一个节点,不涉及剪枝等操作而且当startIndex等于数组长度时不会进入for循环,也会被return掉,所以可以省略终止条件;单层递归逻辑为,首先进入递归函数时result就记录path,因为result要记录每一个节点而不单是叶子节点,然后进入for循环横向遍历,path记录nums[i]然后进入下一层递归,为了避免重复,startIndex取i+1,之后再回溯。
90.子集II
class Solution {
private:
vector<int> path;
vector<vector<int>> result;
vector<bool> used;
void backtracking(vector<int>& nums,int startIndex,vector<bool> used){
result.push_back(path);
for(int i=startIndex;i<nums.size();i++){
if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false) continue;
path.push_back(nums[i]);
used[i]=true;
backtracking(nums,i+1,used);
path.pop_back();
used[i]=false;
}
return;
}
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
path.clear();
result.clear();
used.clear();
sort(nums.begin(),nums.end());
backtracking(nums,0,used);
return result;
}
};
这一版代码是错误的,错误点就是used数组,因为我们直接用数组下标给它赋值了,所以必须要先初始化used数组,而不是简单的创建数组,要初始化空间,初始化初值。
class Solution {
private:
vector<int> path;
vector<vector<int>> result;
void backtracking(vector<int>& nums,int startIndex,vector<bool> used){
result.push_back(path);
for(int i=startIndex;i<nums.size();i++){
if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false) continue;
path.push_back(nums[i]);
used[i]=true;
backtracking(nums,i+1,used);
path.pop_back();
used[i]=false;
}
return;
}
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
path.clear();
result.clear();
vector<bool> used(nums.size(),false);
sort(nums.begin(),nums.end());
backtracking(nums,0,used);
return result;
}
};
这是修改后的代码,这道题是组合问题去重和子集问题的组合,总体思路就是子集问题加上去重思路。去重用到了used数组,标记是否是树枝还是树层重合,used数组所有初值为false,用过了就变为true,进入树枝就是true,回溯回来又变为false。子集问题要记录剪枝后的每一个path,不需要终止条件,进入for循环横向遍历,首先进行剪枝,然后path记录剪枝后的nums[i],used[i]令为true,进入下一层递归,回溯后pop_back,used[i]令为false。