问题描述:
Given a collection of integers that might contain duplicates, nums, return all possible subsets.
Note: The solution set must not contain duplicate subsets.
For example,
If nums = [1,2,2]
, a solution is:
[ [2], [1], [1,2,2], [2,2], [1,2], [] ]
原问题链接:https://leetcode.com/problems/subsets-ii/
问题分析
这个问题有一个比较简单的思路,就是基于前面subsets问题的基础,在每次往列表里加入元素的时候判断一下是否有现存的元素来去除重复。这种方式的代码实现如下:
public class Solution { public List<List<Integer>> subsetsWithDup(int[] num) { Arrays.sort(num); List<List<Integer>> lists = new ArrayList<List<Integer>>(); for(int i = 0; i < 1 << num.length; i++) { List<Integer> list = new ArrayList<Integer>(); for(int j = 0; j < num.length; j++) { if((i & (1 << j)) > 0) list.add(num[j]); } if(!lists.contains(list)) lists.add(list); } return lists; } }
这种方式是基于求所有子集方法的代码加了个重复检查的部分。但是在出现重复元素比较多的情况下会不断的要去list里查找比较,所以性能会相对比较低。
除了上述的办法,其实我们还有一种思路。就是对于一个数组来说,它可以有一种递推的情况。最开始当这个数组有0个元素的时候,它所有的取值就是0个,也就是说我们返回一个空的数组作为它的子集。
而如果这时候有1个元素了呢,那么它就是在原来的基础上增加一个包含有这个元素本身的子集。在这里我们就有了两个集合元素:[[], [1]] 。也就是空集和这个元素本身。如果对这个过程做更加一般化的推导的话,就会发现对于第i个元素来说,它前面的i - 1个元素构成的集合我们已经得到了。相当于已经有了前面i - 1个元素构成的所有子集,那么这个子集对于第i个元素来说相当于是不选择第i个的所有情况。那么对于加入第i个元素的情况来说,就需要将这个元素依次增加到原来的每个元素里作为新的选择情况。
这样,我们就得到了一个一般情况下生成集合子集的一个思路。就是首先取空集放到一个列表里。然后每次取一个元素和这个列表里的元素合并,新生成的结果也加入到列表里。这样列表不断累加,生成了所有的情况。但是在这个问题里,我们还有重复的元素,该怎么来处理呢?
在这里我们需要稍微用一个技巧,首先将这些数组元素排序。这样相同的元素就肯定都挨在一起了。每次取一个位置的元素时就和它前面的元素去比较,如果这个元素和前面的元素不同,说明是一个新的元素,按照前面的思路,需要从头开始去和每个元素合并生成新结果。否则说明它的前一个元素已经和之前的那些元素生成了一遍结果集,它就不应该再去重新生成一遍了,而是应该从它前一个元素和之前所有结果开始归并的地方继续进行合并。要实现这一点,需要用一个变量来记录每次前一个元素合并完之后所在的位置。然后在下一个循环开头的时候判断一下,如果nums[i] != nums[i - 1],则将开始的部分begin = 0,否则就保持begin = 上次合并完所在的位置。
详细的代码实现如下:
public class Solution { public List<List<Integer>> subsetsWithDup(int[] nums) { Arrays.sort(nums); List<List<Integer>> result = new ArrayList<>(); result.add(new ArrayList<>()); int begin = 0; for(int i = 0; i < nums.length; i++){ if(i == 0 || nums[i] != nums[i - 1]) begin = 0; int size = result.size(); for(int j = begin; j < size; j++){ List<Integer> cur = new ArrayList<>(result.get(j)); cur.add(nums[i]); result.add(cur); } begin = size; } return result; } }
这种方法因为减少了对重复元素的检查和数组扫描,它的效率会相对高一些。不过由于子集的生成本身在时间复杂度上是指数级别的。它的时间复杂度依然是O(2 ** n) 。
相关推荐
leetcode写题闪退 #*的多少代表此题的有意思程度 有几题第一次写的时候思绪比较混乱: *****Regular Expression Matching 2014.10.29 对于Find Minimum in Rotated Sorted Array II 和 Find Minimum in Rotated ...
leetcode 1004 leetcode 题目分类 数组 哈希表 链表 堆 栈 队列 字符串 树 Trie BST 图 最小(大)堆 Two Points 滑动窗口 递归 回溯 动态规划 排序 拓扑排序 二分查找 数学 深度优先搜索 ...Subsets
python python_leetcode题解之090_Subsets_II
javascript js_leetcode题解之90-subsets-II.js
c语言基础 c语言_leetcode题解之0090_subsets_ii.zip
python python_leetcode题解之078_Subsets
c c语言_leetcode题解之0078_subsets.zip
javascript js_leetcode题解之78-subsets.js
c语言入门 C语言_leetcode题解之78-subsets.c
- **Subsets**:找出所有可能的子集。 - **Permutation**:生成全排列。 6. **贪心(Greedy)**: - **Jump Game**:跳跃游戏,判断是否能到达数组末尾。 - **Gas Station**:寻找最短的加油路线。 - **Candy*...
java java_leetcode题解之Partition to K Equal Sum Subsets.java
- Subsets: 给定一组可能包含重复元素的整数数组,返回该数组所有可能的子集。 - Word Search: 给定一个m×n的二维字符网格board和一个单词(字符串)word,如果word存在于网格中,则返回true;否则,返回false。 - ...
subsets: 78 - permutation - combination sum: 39, 40, 216 - palindrome partitioning - regex - sudoku solver: 37 排序 - merge sort - quick sort - insertion sort - selection sort - counting sort 位操作 -...
10. **Subsets II .cpp** - 第90题“子集II”,要求找到一个集合的所有不重复子集,解冑通常使用递归或回溯法。 以上代码覆盖了算法设计中的多种重要思想,包括回溯法、动态规划、字符串处理、区间操作和深度/广度...
* Subsets:给定一个数组,返回所有可能的子集。这个题目需要使用回溯算法的思想,将数组分解成更小的子数组,并找到所有可能的子集。 6. 贪心算法 贪心算法是一种非常重要的算法思想,LeetCode 中有很多关于贪心...
- 在 Subsets 题目中,回溯算法用于找出集合的所有可能子集。 14. **递归与迭代**: - 递归是一种常见的编程技巧,它将问题分解为更小的问题,而迭代通常是递归的替代方案,使用循环结构代替递归结构。 - 在 ...
leetcode安卓The_LeetCode_Awakens Notice Naming Rule 资料夹 请把题号(三码) 放在题目前面,并用"_" 隔开,题目中间有空格,请用“-” 隔开 (ex: 078_Subsets) 题号请参考 如果没有题号,就先省略 档案 Owner 整理...
【Python LeetCode 面试题解之第78题 子集】 在LeetCode平台上的第78题,名为“Subsets”(子集),是...同时,也可以通过练习LeetCode上的其他相关题目,如“Subsets II”(子集II,考虑重复元素)来提升自己的能力。
3. LeetCode第78题:"Subsets"(子集):给定一个没有重复元素的整数数组,找出所有可能的子集(即功率集)。 4. LeetCode第316题:"Remove Duplicate Letters"(移除重复的字母):给定一个字符串,从中移除重复的...