1. 问题描述:
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets-ii
2. 思路分析:
① 这道题目与78题类似,也是使用递归来求解,只是这里多出了一个数据的限制条件,得到的子集是不可以重复,假如使用两个分支进行递归的话那么可以借助于字符串 + HashSet来对最终得到的子集进行判断,这就要求对数组进行排序,这样才可以使用字符串与HashSet进行去重,因为排序之后得到的记录字符串是有序的,所以之前存在了当前这个子集了说明就重复了,所以只需要加进Set中没有重复的字符串即可
② 使用for循环进行递归的话那么可以在for循环的一开始进行判断,假如发现同一层存在着相同的元素的话那么直接continue尝试下一个元素即可,结合具体的例子就比较好理解了,比如像1,2,2集合同一层的两个2拿取出来都是一样的,形成的子集也是一样的,因为是在for循环中进行递归,所以可以判断排序之后相邻的元素是否重复,这也是去重的关键,这也是与①中方法去重的不一样的点,这里去重的话更方便,而且更重要的是对于重复元素我们是不往下递归的所以也节省了效率,在递归方法的一开始表示的可以将记录中间结果的栈中的元素加入到List结果集中,因为对于可以拿取当前元素与不拿取当前元素往下递归其实就是一种方案
3. 代码如下:
Set去重:
class Solution {
List<List<Integer>> res = new ArrayList<>();
Set<String> set = new HashSet<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
/*需要先排序*/
Arrays.sort(nums);
recursion(nums, 0, new Stack<Integer>(), "");
return res;
}
private void recursion(int[] nums, int pos, Stack<Integer> stack, String cur) {
if (pos == nums.length) {
if (!set.contains(cur)) {
res.add(new ArrayList<>(stack));
set.add(cur);
}
return;
}
stack.add(nums[pos]);
recursion(nums, pos + 1, stack, cur + nums[pos]);
stack.pop();
recursion(nums, pos + 1, stack, cur);
}
}
for循环递归中判断相邻元素去重(先要对数组进行排序):
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
recursion(nums, 0, new Stack<Integer>());
return res;
}
private void recursion(int[] nums, int pos, Stack<Integer> stack) {
/*下面这一句代码也是核心*/
res.add(new ArrayList<>(stack));
for (int i = pos; i < nums.length; ++i) {
/*去重: 也是核心*/
if (i > pos && nums[i] == nums[i - 1]) continue;
stack.push(nums[i]);
recursion(nums, i + 1, stack);
stack.pop();
}
}
}