1
- # 题目地址
2
- https://leetcode-cn.com/problems/subsets-ii/
3
1
4
- # 第90题. 子集II
2
+ > 子集问题加去重!
3
+
4
+ # 第90题.子集II
5
+
6
+ 题目链接:https://leetcode-cn.com/problems/subsets-ii/
5
7
6
8
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
7
9
8
10
说明:解集不能包含重复的子集。
9
11
10
- 示例:
12
+ 示例:
11
13
输入: [ 1,2,2]
12
14
输出:
13
15
[
@@ -22,56 +24,65 @@ https://leetcode-cn.com/problems/subsets-ii/
22
24
23
25
# 思路
24
26
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 ) 。
28
28
29
- 这个去重为什么很难理解呢, ** 所谓去重,其实就是使用过的元素不能重复选取。 ** 这么一说好像很简单!
29
+ 这道题目和 [ 回溯算法:求子集问题! ] ( https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA ) 区别就是集合里有重复元素了,而且求取的子集要去重。
30
30
31
- 都知道组合问题可以抽象为树形结构,那么“使用过”在这个树形结构上是有两个维度的,一个维度是同一树枝上使用过,一个维度是同一树层上使用过。 ** 没有理解这两个层面上的“使用过” 是造成大家没有彻底理解去重的根本原因。 **
31
+ 那么关于回溯算法中的去重问题, ** 在 [ 40.组合总和II ] ( https://mp.weixin.qq.com/s/_1zPYk70NvHsdY8UWVGXmQ ) 中已经详细讲解过了,和本题是一个套路 ** 。
32
32
33
- 所以要明确我们要去重的是同一树层上的“使用过” 。
33
+ ** 剧透一下,后期要讲解的排列问题里去重也是这个套路,所以理解“树层去重”和“树枝去重”非常重要 ** 。
34
34
35
- ** 还要强调的是去重一定要对元素经行排序,这样我们才方便通过相邻的节点来判断是否重复使用了。**
36
-
37
- 用示例中的[ 1, 2, 2] 来举例,如图所示:
35
+ 用示例中的[ 1, 2, 2] 来举例,如图所示: (** 注意去重需要先对集合排序** )
38
36
39
37
<img src =' ../pics/90.子集II.png ' width =600 > </img ></div >
40
38
41
- 从图中可以看出,同一树层上对重复取2 就要过滤掉,同一树枝上就可以重复取2,因为同一树枝上元素的集合才是唯一子集!
39
+ 从图中可以看出,同一树层上重复取2 就要过滤掉,同一树枝上就可以重复取2,因为同一树枝上元素的集合才是唯一子集!
42
40
43
- 代码如下:(已经详细注释)
41
+ 本题就是其实就是 [ 回溯算法:求子集问题! ] ( https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA ) 的基础上加上了去重,去重我们在 [ 回溯算法:求组合总和(三) ] ( https://mp.weixin.qq.com/s/_1zPYk70NvHsdY8UWVGXmQ ) 也讲过了,所以我就直接给出代码了:
44
42
45
43
# C++代码
46
44
47
45
```
48
46
class Solution {
49
47
private:
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);
52
52
for (int i = startIndex; i < nums.size(); i++) {
53
- // used[i - 1] == true,说明同一树支candidates[i - 1]使用过
53
+ // used[i - 1] == true,说明同一树支candidates[i - 1]使用过
54
54
// used[i - 1] == false,说明同一树层candidates[i - 1]使用过
55
55
// 而我们要对同一树层使用过的元素进行跳过
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) {
57
57
continue;
58
58
}
59
- vec .push_back(nums[i]);
59
+ path .push_back(nums[i]);
60
60
used[i] = true;
61
- backtracking(nums, result, vec, i + 1, used);
61
+ backtracking(nums, i + 1, used);
62
62
used[i] = false;
63
- vec .pop_back();
63
+ path .pop_back();
64
64
}
65
65
}
66
66
67
67
public:
68
68
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
69
+ result.clear();
70
+ path.clear();
69
71
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);
74
74
return result;
75
75
}
76
76
};
77
+
77
78
```
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