90.子集 II
给你一个整数数组 nums
,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
示例 1:
输入: nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
示例 2:
输入: nums = [0]
输出:[[],[0]]
提示:
- 1 ≤ n u m s . l e n g t h ≤ 10 1 \leq nums.length \leq 10 1≤nums.length≤10
- − 10 ≤ n u m s [ i ] ≤ 10 -10 \leq nums[i] \leq 10 −10≤nums[i]≤10
解法一(回溯)
思路分析:
- 子集问题,一般使用回溯算法来求解
- 考虑回溯函数三要素:
- 首先考虑回溯函数返回值和参数,因为不需要特意返回某值,即返回值类型为
void
,然后对于参数,即集合参数,以及下一层递归选取元素的起始索引 - 然后确定回溯的结束条件,因为子集问题求得是所有节点的结果,即不需要特意使用
return;
来结束递归 - 然后考虑回溯的遍历集合过程,因为集合中会出现重复元素,所以考虑对集合进行排序,然后对树层进行去重,然后再选取符合题意的元素,进行递归,递归后回溯
- 首先考虑回溯函数返回值和参数,因为不需要特意返回某值,即返回值类型为
- 通过对给定集合进行排序,从而便于在 回溯的树形结构中,树层的层面上进行去重
实现代码如下:
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums); // 对集合进行排序 便于树层去重
backtracking(0, nums);
return ans;
}
private void backtracking(int startIndex, int[] nums) {
ans.add(new ArrayList<>(path)); // 保存每个节点的结果
for (int i = startIndex; i < nums.length; ++ i) {
if (i != startIndex && nums[i] == nums[i-1])
continue; // 树层去重
path.add(nums[i]); // 添加元素
backtracking(i+1, nums); // 继续递归
path.remove(path.size() - 1); // 回溯
}
}
}
提交结果如下:
解答成功:
执行耗时:1 ms,击败了97.90% 的Java用户
内存消耗:42.5 MB,击败了50.88% 的Java用户
复杂度分析:
- 时间复杂度: O ( n × 2 n ) O(n \times 2^n) O(n×2n),即有 2 n 2^n 2n个状态,而每得到一个状态需要花费 O ( n ) O(n) O(n)
- 空间复杂度: O ( n ) O(n) O(n)