[LeetCode] 每日一题 90. 子集 II
题目链接
题目描述
给你一个整数数组 nums
,其中可能包含重复元素,请你返回该数组所有可能的
子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
子集
数组的 子集 是从数组中选择一些元素(可能为空)。
示例输入
示例 1
输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
示例 2
输入:nums = [0]
输出:[[],[0]]
提示
1 <= nums.length <= 10
-10 <= nums[i] <= 10
解题思路
本题要求返回一个整数数组的所有可能子集,其中可能包含重复的元素。为了避免重复的子集,我们需要在生成子集时采取有效的去重措施
回溯算法:
排序:首先将输入数组排序,这样相同的元素会被放在相邻的位置,方便去重
回溯:从每个元素开始,尝试包括当前元素或者不包括它。每次尝试时,记录当前的路径
去重条件:如果当前元素和前一个元素相同且前一个元素没有被使用过,说明当前元素是重复的,因此跳过当前元素。这一步有效地避免了重复子集的产生
路径存储:每生成一个子集,就将其加入到结果集中。需要注意的是,路径的内容是以当前状态为基础不断扩展的
代码实现
class Solution {
boolean[] used;
List<Integer> path = new ArrayList<>();
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
used = new boolean[nums.length];
backtrack(nums, 0);
return res;
}
private void backtrack(int[] nums, int startIndex) {
res.add(new ArrayList<>(path));
for (int i = startIndex; i < nums.length; i++) {
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
continue;
}
used[i] = true;
path.add(nums[i]);
backtrack(nums, i + 1);
path.remove(path.size() - 1);
used[i] = false;
}
}
}
复杂度分析
时间复杂度:O(2^n),其中
n
是数组的长度。在最坏情况下,我们会生成所有的子集,而子集的数量是2^n
。因此,时间复杂度为 O(2^n)空间复杂度:O(n),用于存储路径和递归栈的空间。递归的深度最多为
n
,而路径的长度也最大为n
,因此空间复杂度是 O(n)
总结
本题通过回溯算法生成所有可能的子集,并且通过排序和去重条件有效地避免了重复子集的出现。回溯算法能够递归地探索每一种可能的选择,通过逐步增加元素到路径中,并在每次递归结束后撤销选择。
这种方式不仅能解决问题,而且具有较高的效率,特别是在处理有重复元素的情况下,通过合理的去重条件使得算法避免了重复计算。
希望这篇分享能为你带来启发!如果你有任何问题或建议,欢迎在评论区留言,与我共同交流探讨。