mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-24 00:57:05 +08:00
246 lines
7.3 KiB
Markdown
246 lines
7.3 KiB
Markdown
<p align="center">
|
||
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
|
||
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
|
||
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
|
||
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
|
||
</p>
|
||
<p align="center"><strong>欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||
|
||
|
||
## 第90题.子集II
|
||
|
||
题目链接:https://leetcode-cn.com/problems/subsets-ii/
|
||
|
||
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
|
||
|
||
说明:解集不能包含重复的子集。
|
||
|
||
示例:
|
||
输入: [1,2,2]
|
||
输出:
|
||
[
|
||
[2],
|
||
[1],
|
||
[1,2,2],
|
||
[2,2],
|
||
[1,2],
|
||
[]
|
||
]
|
||
|
||
|
||
## 思路
|
||
|
||
做本题之前一定要先做[78.子集](https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA)。
|
||
|
||
这道题目和[回溯算法:求子集问题!](https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA)区别就是集合里有重复元素了,而且求取的子集要去重。
|
||
|
||
那么关于回溯算法中的去重问题,**在[40.组合总和II](https://mp.weixin.qq.com/s/_1zPYk70NvHsdY8UWVGXmQ)中已经详细讲解过了,和本题是一个套路**。
|
||
|
||
**剧透一下,后期要讲解的排列问题里去重也是这个套路,所以理解“树层去重”和“树枝去重”非常重要**。
|
||
|
||
用示例中的[1, 2, 2] 来举例,如图所示: (**注意去重需要先对集合排序**)
|
||
|
||

|
||
|
||
从图中可以看出,同一树层上重复取2 就要过滤掉,同一树枝上就可以重复取2,因为同一树枝上元素的集合才是唯一子集!
|
||
|
||
本题就是其实就是[回溯算法:求子集问题!](https://mp.weixin.qq.com/s/NNRzX-vJ_pjK4qxohd_LtA)的基础上加上了去重,去重我们在[回溯算法:求组合总和(三)](https://mp.weixin.qq.com/s/_1zPYk70NvHsdY8UWVGXmQ)也讲过了,所以我就直接给出代码了:
|
||
|
||
## C++代码
|
||
|
||
```
|
||
class Solution {
|
||
private:
|
||
vector<vector<int>> result;
|
||
vector<int> path;
|
||
void backtracking(vector<int>& nums, int startIndex, vector<bool>& used) {
|
||
result.push_back(path);
|
||
for (int i = startIndex; i < nums.size(); i++) {
|
||
// used[i - 1] == true,说明同一树支candidates[i - 1]使用过
|
||
// used[i - 1] == false,说明同一树层candidates[i - 1]使用过
|
||
// 而我们要对同一树层使用过的元素进行跳过
|
||
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
|
||
continue;
|
||
}
|
||
path.push_back(nums[i]);
|
||
used[i] = true;
|
||
backtracking(nums, i + 1, used);
|
||
used[i] = false;
|
||
path.pop_back();
|
||
}
|
||
}
|
||
|
||
public:
|
||
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
|
||
result.clear();
|
||
path.clear();
|
||
vector<bool> used(nums.size(), false);
|
||
sort(nums.begin(), nums.end()); // 去重需要排序
|
||
backtracking(nums, 0, used);
|
||
return result;
|
||
}
|
||
};
|
||
|
||
```
|
||
|
||
使用set去重的版本。
|
||
```
|
||
class Solution {
|
||
private:
|
||
vector<vector<int>> result;
|
||
vector<int> path;
|
||
void backtracking(vector<int>& nums, int startIndex, vector<bool>& used) {
|
||
result.push_back(path);
|
||
unordered_set<int> uset;
|
||
for (int i = startIndex; i < nums.size(); i++) {
|
||
if (uset.find(nums[i]) != uset.end()) {
|
||
continue;
|
||
}
|
||
uset.insert(nums[i]);
|
||
path.push_back(nums[i]);
|
||
backtracking(nums, i + 1, used);
|
||
path.pop_back();
|
||
}
|
||
}
|
||
|
||
public:
|
||
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
|
||
result.clear();
|
||
path.clear();
|
||
vector<bool> used(nums.size(), false);
|
||
sort(nums.begin(), nums.end()); // 去重需要排序
|
||
backtracking(nums, 0, used);
|
||
return result;
|
||
}
|
||
};
|
||
|
||
```
|
||
|
||
## 补充
|
||
|
||
本题也可以不适用used数组来去重,因为递归的时候下一个startIndex是i+1而不是0。
|
||
|
||
如果要是全排列的话,每次要从0开始遍历,为了跳过已入栈的元素,需要使用used。
|
||
|
||
代码如下:
|
||
|
||
```C++
|
||
class Solution {
|
||
private:
|
||
vector<vector<int>> result;
|
||
vector<int> path;
|
||
void backtracking(vector<int>& nums, int startIndex) {
|
||
result.push_back(path);
|
||
for (int i = startIndex; i < nums.size(); i++) {
|
||
// 而我们要对同一树层使用过的元素进行跳过
|
||
if (i > startIndex && nums[i] == nums[i - 1] ) { // 注意这里使用i > startIndex
|
||
continue;
|
||
}
|
||
path.push_back(nums[i]);
|
||
backtracking(nums, i + 1);
|
||
path.pop_back();
|
||
}
|
||
}
|
||
|
||
public:
|
||
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
|
||
result.clear();
|
||
path.clear();
|
||
sort(nums.begin(), nums.end()); // 去重需要排序
|
||
backtracking(nums, 0);
|
||
return result;
|
||
}
|
||
};
|
||
|
||
```
|
||
|
||
## 总结
|
||
|
||
其实这道题目的知识点,我们之前都讲过了,如果之前讲过的子集问题和去重问题都掌握的好,这道题目应该分分钟AC。
|
||
|
||
当然本题去重的逻辑,也可以这么写
|
||
|
||
```
|
||
if (i > startIndex && nums[i] == nums[i - 1] ) {
|
||
continue;
|
||
}
|
||
```
|
||
|
||
|
||
|
||
## 其他语言版本
|
||
|
||
|
||
Java:
|
||
```java
|
||
class Solution {
|
||
List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合
|
||
LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果
|
||
boolean[] used;
|
||
public List<List<Integer>> subsetsWithDup(int[] nums) {
|
||
if (nums.length == 0){
|
||
result.add(path);
|
||
return result;
|
||
}
|
||
Arrays.sort(nums);
|
||
used = new boolean[nums.length];
|
||
subsetsWithDupHelper(nums, 0);
|
||
return result;
|
||
}
|
||
|
||
private void subsetsWithDupHelper(int[] nums, int startIndex){
|
||
result.add(new ArrayList<>(path));
|
||
if (startIndex >= nums.length){
|
||
return;
|
||
}
|
||
for (int i = startIndex; i < nums.length; i++){
|
||
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]){
|
||
continue;
|
||
}
|
||
path.add(nums[i]);
|
||
used[i] = true;
|
||
subsetsWithDupHelper(nums, i + 1);
|
||
path.removeLast();
|
||
used[i] = false;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
Python:
|
||
|
||
|
||
Go:
|
||
```Go
|
||
var res[][]int
|
||
func subsetsWithDup(nums []int)[][]int {
|
||
res=make([][]int,0)
|
||
sort.Ints(nums)
|
||
dfs([]int{},nums,0)
|
||
return res
|
||
}
|
||
|
||
func dfs(temp, num []int, start int) {
|
||
tmp:=make([]int,len(temp))
|
||
copy(tmp,temp)
|
||
|
||
res=append(res,tmp)
|
||
for i:=start;i<len(num);i++{
|
||
if i>start&&num[i]==num[i-1]{
|
||
continue
|
||
}
|
||
temp=append(temp,num[i])
|
||
dfs(temp,num,i+1)
|
||
temp=temp[:len(temp)-1]
|
||
}
|
||
}
|
||
```
|
||
|
||
|
||
|
||
-----------------------
|
||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
|
||
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>
|