1- # 题目地址
2- https://leetcode-cn.com/problems/subsets-ii/
31
4- # 第90题. 子集II
2+ > 子集问题加去重!
3+
4+ # 第90题.子集II
5+
6+ 题目链接:https://leetcode-cn.com/problems/subsets-ii/
57
68给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
79
810说明:解集不能包含重复的子集。
911
10- 示例:
12+ 示例:
1113输入: [ 1,2,2]
1214输出:
1315[
@@ -22,56 +24,65 @@ https://leetcode-cn.com/problems/subsets-ii/
2224
2325# 思路
2426
25- 这道题目和[ 0078.子集] ( https://github.com/youngyangyang04/leetcode/blob/master/problems/0078.子集.md ) 区别就是集合里有重复元素了,而且求取的子集要去重。
26-
27- 很多同学在去重上想不明白,其实很多题解也没有讲清楚,反正代码是能过的,感觉是那么回事,稀里糊涂的先把题目过了。
27+ 做本题之前一定要先做[ 78.子集] ( https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA ) 。
2828
29- 这个去重为什么很难理解呢, ** 所谓去重,其实就是使用过的元素不能重复选取。 ** 这么一说好像很简单!
29+ 这道题目和 [ 回溯算法:求子集问题! ] ( https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA ) 区别就是集合里有重复元素了,而且求取的子集要去重。
3030
31- 都知道组合问题可以抽象为树形结构,那么“使用过”在这个树形结构上是有两个维度的,一个维度是同一树枝上使用过,一个维度是同一树层上使用过。 ** 没有理解这两个层面上的“使用过” 是造成大家没有彻底理解去重的根本原因。 **
31+ 那么关于回溯算法中的去重问题, ** 在 [ 40.组合总和II ] ( https://mp.weixin.qq.com/s/_1zPYk70NvHsdY8UWVGXmQ ) 中已经详细讲解过了,和本题是一个套路 ** 。
3232
33- 所以要明确我们要去重的是同一树层上的“使用过” 。
33+ ** 剧透一下,后期要讲解的排列问题里去重也是这个套路,所以理解“树层去重”和“树枝去重”非常重要 ** 。
3434
35- ** 还要强调的是去重一定要对元素经行排序,这样我们才方便通过相邻的节点来判断是否重复使用了。**
36-
37- 用示例中的[ 1, 2, 2] 来举例,如图所示:
35+ 用示例中的[ 1, 2, 2] 来举例,如图所示: (** 注意去重需要先对集合排序** )
3836
3937<img src =' ../pics/90.子集II.png ' width =600 > </img ></div >
4038
41- 从图中可以看出,同一树层上对重复取2 就要过滤掉,同一树枝上就可以重复取2,因为同一树枝上元素的集合才是唯一子集!
39+ 从图中可以看出,同一树层上重复取2 就要过滤掉,同一树枝上就可以重复取2,因为同一树枝上元素的集合才是唯一子集!
4240
43- 代码如下:(已经详细注释)
41+ 本题就是其实就是 [ 回溯算法:求子集问题! ] ( https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA ) 的基础上加上了去重,去重我们在 [ 回溯算法:求组合总和(三) ] ( https://mp.weixin.qq.com/s/_1zPYk70NvHsdY8UWVGXmQ ) 也讲过了,所以我就直接给出代码了:
4442
4543# C++代码
4644
4745```
4846class Solution {
4947private:
50- void backtracking(vector<int>& nums, vector<vector<int>>& result, vector<int>& vec, int startIndex, vector<bool>& used) {
51- result.push_back(vec);
48+ vector<vector<int>> result;
49+ vector<int> path;
50+ void backtracking(vector<int>& nums, int startIndex, vector<bool>& used) {
51+ result.push_back(path);
5252 for (int i = startIndex; i < nums.size(); i++) {
53- // used[i - 1] == true,说明同一树支candidates[i - 1]使用过
53+ // used[i - 1] == true,说明同一树支candidates[i - 1]使用过
5454 // used[i - 1] == false,说明同一树层candidates[i - 1]使用过
5555 // 而我们要对同一树层使用过的元素进行跳过
56- if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
56+ if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
5757 continue;
5858 }
59- vec .push_back(nums[i]);
59+ path .push_back(nums[i]);
6060 used[i] = true;
61- backtracking(nums, result, vec, i + 1, used);
61+ backtracking(nums, i + 1, used);
6262 used[i] = false;
63- vec .pop_back();
63+ path .pop_back();
6464 }
6565 }
6666
6767public:
6868 vector<vector<int>> subsetsWithDup(vector<int>& nums) {
69+ result.clear();
70+ path.clear();
6971 vector<bool> used(nums.size(), false);
70- vector<vector<int>> result;
71- vector<int> vec;
72- sort(nums.begin(), nums.end());
73- backtracking(nums, result, vec, 0, used);
72+ sort(nums.begin(), nums.end()); // 去重需要排序
73+ backtracking(nums, 0, used);
7474 return result;
7575 }
7676};
77+
7778```
79+
80+ # 总结
81+
82+ 其实这道题目的知识点,我们之前都讲过了,如果之前讲过的子集问题和去重问题都掌握的好,这道题目应该分分钟AC。
83+
84+ ** 就酱,如果感觉融会贯通了,就把「代码随想录」介绍给自己的同学朋友吧,也许他们也需要!**
85+
86+ > 我是[ 程序员Carl] ( https://github.com/youngyangyang04 ) ,组队刷题可以找我,本文[ leetcode刷题攻略] ( https://github.com/youngyangyang04/leetcode-master ) 已收录,更多[ 精彩算法文章] ( https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzUxNjY5NTYxNA==&action=getalbum&album_id=1485825793120387074&scene=173#wechat_redirect ) 尽在:[ 代码随想录] ( https://img-blog.csdnimg.cn/20200815195519696.png ) ,期待你的关注!
87+
88+
0 commit comments