LeetCode:子集 II【90】
题目描述
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2] 输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]
题目分析
1.可能包含重复元素的整形数组,返回该数组的所有子集,但结果不能包含重复。
这句话的意思是结果集中的每一个元素都是独一无二的,如果我们用暴力递归的话,就会出现很多重复元素,比如{1,2}就会出现多次,我们可能会想到使用Set来保存结果,但是这个结果的复杂度是很高的,我们有没有一种办法避免重复元素的出现呢?
题解中的框架是一种通用的解决方案,可以解决相当多的递归回溯问题。对于重复元素,我们可以首先对数组元素进行排序,这样所有的重复的元素就会出现在一起了。
2.那具体的搜索策略是怎样的呢?
3.递归与回溯的过程大致是这样的:
每次回溯开始时,加入当前临时列表,比如刚开始临时列表为空,所以加入空集;接下来从第start元素开始循环往后,即第一层分了三条路,但是第二条路和第三条路的临时列表是相同的,只走他们中的一条路即可,为什么是这样的呢?如果我们不走其中一条,他们往后构建的所有临时列表都是一样的,因为都是以他们为前缀{1,2},我们就没必要做重复工作了。第二层的第一块也是这个道理,如果我们选了第一条路临时列表为{1,2},我们在走第二条虚线路时前缀也是{1,2},那么从这两个元素为前缀构成的所有临时列表都是重复的(我们同样要选取后面的所有元素的所有可能),所以我们跳过即可。
关于临时列表,就是来保存一个子集,详看代码。
Java题解
class Solution { public List<List<Integer>> subsetsWithDup(int[] nums) { List<List<Integer>> list = new ArrayList<>(); Arrays.sort(nums); backtrack(list, new ArrayList<>(), nums, 0); return list; } private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int start){ list.add(new ArrayList<>(tempList)); for(int i = start; i < nums.length; i++){ if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates tempList.add(nums[i]); backtrack(list, tempList, nums, i + 1); tempList.remove(tempList.size() - 1); } } }