78. 子集
题目描述
给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的 子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
思路分析
我一开始的想法是通过一个length
来控制每轮回溯的长度,也就是如下面代码所示:
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
void backtrace(int idx, int length, const vector<int>& nums){
if(path.size() == length){
ans.push_back(path);
return;
}
for(int i = idx; i < nums.size(); ++i){
path.push_back(nums[i]);
backtrace(i + 1, length, nums);
path.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
for(int j = 0; j <= nums.size(); ++j){
backtrace(0, j, nums);
}
return ans;
}
};
如果把回溯过程看成一棵二叉树:
上面这种思路仍然是在叶子节点处收获结果(与组合/分割问题相同),因为是将整体问题利用length
分割成了一个一个子问题再进行回溯求解。
实际上这题整体上是在每一个节点处收获果实。那么我们收获结果的方式就和之前的回溯有些不同,具体请看代码:
cpp代码
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
void backtrace(int idx, const vector<int>& nums){
ans.push_back(path); // 进入递归则收获结果
// if其实可以不写
if(idx >= nums.size()){
return;
}
for(int i = idx; i < nums.size(); ++i){
path.push_back(nums[i]);
backtrace(i + 1, nums);
path.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
backtrace(0, nums);
return ans;
}
};
90. 子集 II
题目描述
给你一个整数数组 nums
,其中可能包含重复元素,请你返回该数组所有可能的 子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
思路分析
这题和上题的区别在于nums
中包含重复元素,也就是说如果我们按照上题的解题思路,答案中就会包含重复的子集,所以在这题中,我们要考虑到这一点。
实际上这题是 上题 和 组合总和II的一个结合。
我们要去除的是同一层节点的重复数,而不是同一个树枝上的重复数,所以需要用一个used
数组来区分。回溯后的节点used[i] = 0
,而还未回溯的节点used[i] = 1
。具体看代码。
cpp代码
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
void backtrace(int startIndex, const vector<int>& nums, vector<int>& used){
ans.push_back(path);
for(int i = startIndex; i < nums.size(); ++i){
// 剪枝去重
if(i > 0 && nums[i] == nums[i-1] && used[i-1] == 0){
continue;
}
path.push_back(nums[i]);
used[i] = 1;
backtrace(i + 1, nums, used);
path.pop_back();
used[i] = 0;
}
}
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<int> used(nums.size(), 0);
backtrace(0, nums, used);
return ans;
}
};
491. 非递减子序列
题目描述
给你一个整数数组 nums
,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
思路分析
这题和子集II很像,但是需要注意的是这题不能对给定的数组进行排序,所以我们在判断树层重复的时候不能和上题一样。并且还要多一个和前一个选择的元素比较大小的条件。
下面是用的是哈希数组去重:
cpp代码
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
void backtrace(int idx, const vector<int>& nums){
if(path.size() >= 2){
ans.push_back(path);
}
int used[201] = {0}; // 哈希数组
for(int i = idx; i < nums.size(); ++i){
if((path.size() > 0 && nums[i] < path[path.size()-1]) ||
used[nums[i] + 100] != 0){
continue;
}
used[nums[i] + 100] = 1;
path.push_back(nums[i]);
backtrace(i + 1, nums);
path.pop_back();
}
}
vector<vector<int>> findSubsequences(vector<int>& nums) {
ans.clear();
path.clear();
backtrace(0, nums);
return ans;
}
};