(一)题目描述
给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
(二) 解题思路
利用回溯+递归,该题与单纯求子集有所不同,该题所给集合有重复元素,所以所得结果就涉及去重问题。去重分两种:第一,树层去重(就是该题):即同一层的元素不能重复选取,基本去重思想即如果nums[i]==nums[i-1]&&flag[i-1]=false(该句话的含义即为:如果同一层元素有相同的,那么在选取同一层的下一个元素时,同层党的上一个元素必定刚出栈,所以其标志为false,若为同树枝的不同层元素,因下一个元素被选择时上一个元素不必出栈,所以其标志为true)
(三)代码实现
class Solution28 {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> res=new ArrayList<>();
ArrayList path = new ArrayList<>();
Arrays.sort(nums);
int len=nums.length;
boolean[] flag=new boolean[len];
dfs(nums,res,path,0,len,flag);
return res;
}
public void dfs(int[] nums,List<List<Integer>> res,ArrayList path,int k,int len,boolean[] flag){
//每递归一次,就将上一次得到的新的路径加入到res中
res.add(new ArrayList<>(path)); //复制一份path数组,并加入res中
// 如果已经遍历到数组尾部,则回退
if(k==len){
return;
}
for (int i = k; i < len; i++) {
// 如果同一层有重复元素,则剪枝
if(i>=0&&nums[i]==nums[i-1]&&flag[i]==false){
continue;
}
//添加元素进路径并标记
path.add(nums[i]);
flag[i]=true;
dfs(nums,res,path,i+1,len,flag);
//递归回退,恢复到递归前的状态
flag[i]=false;
path.remove(path.size()-1);
}
}
}
补充知识点:
res.add(new ArrayList<>(path));//添加path的副本到res,下一次path的改变不会影响res数组
res.add(path);//添加path到res数组,path的改变会影响res数组中原来添加的path的值,如path={1,2,3},添加后res={{1,2,3}},之后path变为{1,2,3,4},则再添加一次path后,res变为{{1,2,3,4},{1,2,3,4}},而上面的写法res为{{1,2,3},{1,2,3,4}}