目录
1,题目描述
英文描述
Given a collection of integers that might contain duplicates, nums, return all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
Example:
Input: [1,2,2]
Output:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
中文描述
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
2,解题思路
- 假设数组中无重复元素,比如nums=[1,2,3,4],递归过程如下:
- 从根节点(这里指第二层中的节点,顶端的空节点是为了将各个树连接起来)到任意节点形成的路径,即为输出结果的一部分。比如根节点为1时,[1]、[1,2]、[1,2,3]、[1,2,3,4]、[1,2,4]、[1,3]、[1,3,4]、[1,4]均为结果集中的一部分;
- 假设数组中有重复元素时,比如nums=[1,2,2,4],不加剪枝的递归过程如下:
- 可以看出绿色部分是递归过程中的重复部分,仍以根节点1为例,[1]、[1,2]、[1,2,2]、[1,2,2,4]、[1,2,4]、[1,2]、[1,2,4]、[1,4];
- 出现重复的原因是,相邻两个元素相同,故可以想到,遍历到相同元素时将其跳过(剪枝),从而避免重复;
- 关键代码如下:
for(int i=depth; i<v.size(); ++i) { if(i > depth && v[i] == v[i-1]) continue; // 剪枝 cur.push_back(v[i]); // 入栈 dfs(i+1); // 深度加一 cur.pop_back(); // 出栈(配对出现) }
3,AC代码
class Solution {
public:
vector<vector<int>> ans;
vector<int> cur; // 当前子集元素
vector<int> v; // 全局变量 对原数组进行拷贝 从而不需借助参数传递
void dfs(int depth) {
ans.push_back(cur);
if(depth == v.size()) return; // 递归出口
for(int i=depth; i<v.size(); ++i) {
if(i > depth && v[i] == v[i-1]) continue; // 剪枝
cur.push_back(v[i]); // 入栈
dfs(i+1); // 深度加一
cur.pop_back(); // 出栈(配对出现)
}
}
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(), nums.end()); // 排序 便于剪枝
v = nums;
dfs(0);
return ans;
}
};
4,解题过程
因为不允许有重复的子集,一开始就想到用set处理,其余按照正常递归来写,直接超时。现在想想确实很憨(lll¬ω¬)
看了大神的代码,豁然开朗,对原数组进行排序,重复的元素必定排列在一起,所以递归时跳过即可避免重复;