思路:本题相比78子集,可能会出现重复的集合元素,且最后要求的子集不能出现重复,所以需要对子集进行去重操作,因为回溯问题常常可以抽象成一颗N叉树,本题的去重则要求二叉树的树枝间可以出现重复的元素,但是同一层不能出现相同的元素,所以我们可以使用used数组来进行去重,used数组为false的时候表示为同一树层元素,used数组为true的时候表示为同一树枝的元素。当然去重同一树层的重复元素还有另一种不用使用used数组的方法 用i>start 进行判断.
这里关于used数组的表示可参照卡尔大佬的图
used数组判断重复的示例.png
注意这里计算used数组的时候要看当前的父节点,而不是兄弟节点
class Solution {
//在78子集的基础上怎加去重的操作
List<List<Integer>> res = new ArrayList<>();
List<Integer> list = new LinkedList<>();
// 去重也可以使用used数组
boolean[] used;
public List<List<Integer>> subsetsWithDup(int[] nums) {
used = new boolean[nums.length];
Arrays.sort(nums);
backTracking(nums,0);
return res;
}
public void backTracking(int[] nums,int start){
res.add(new ArrayList<>(list));
if (start >= nums.length){
return;
}
for (int i = start; i < nums.length;i++){
// 跳过当前树层使用过的、相同的元素
if(i > start && nums[i] == nums[i - 1]) continue;
// 使用used数组排除同一树层使用过的相同元素
// if(i > 0 && nums[i] == nums[i - 1]&& used[i] == false) continue;
list.add(nums[i]);
// used[i] = true;
backTracking(nums,i + 1);
// used[i] = false;
list.remove(list.size() -1 );
}
}
}
思路:本题是一道易错题,看起来和子集很像,只需要在子集的基础上,增加添加到结果集的时候判断长度的逻辑即可,但是本题是不能先进行排序再进行回溯的,因为排序之后就变成了递增数组,和题意不符合,出现以下的错误
错误示例.png
所以本题应该单独的增加判断递增的逻辑和进行去重的方法,不能使用之前排序去重的那一套代码。判断递增:判断当前nums[i]是否大于list的最后一个元素即可。
去重:使用map进行去重,key-value对于nums[i]和出现的次数
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> list = new LinkedList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backTracking(nums,0);
return res;
}
public void backTracking(int[] nums,int start){
if (list.size() > 1){
res.add(new ArrayList<>(list));
}
// map每次递归都会被刷新 不会影响到下一层的情况
Map<Integer,Integer> map = new HashMap<>();
for (int i = start; i < nums.length;i++){
// 保证是递增的
if(!list.isEmpty() && nums[i] < list.getLast()) continue;
// 用hashmap去重,同一个数字在一层只能出现一次
if (map.getOrDefault(nums[i],0) >= 1) continue;
map.put(nums[i],map.getOrDefault(nums[i],0) + 1 );
list.add(nums[i]);
backTracking(nums,i + 1);
list.remove(list.size() -1 );
}
}
}