leecode/problems/90.subsets-ii.md
2020-05-22 18:17:19 +08:00

175 lines
4.3 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 题目地址
https://leetcode.com/problems/subsets-ii/description/
## 题目描述
```
Given a collection of integers that might contain duplicates, nums, return all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
Example:
Input: [1,2,2]
Output:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
```
## 思路
这道题目是求集合,并不是`求极值`,因此动态规划不是特别切合,因此我们需要考虑别的方法。
这种题目其实有一个通用的解法,就是回溯法。
网上也有大神给出了这种回溯法解题的
[通用写法](https://leetcode.com/problems/combination-sum/discuss/16502/A-general-approach-to-backtracking-questions-in-Java-(Subsets-Permutations-Combination-Sum-Palindrome-Partitioning)),这里的所有的解法使用通用方法解答。
除了这道题目还有很多其他题目可以用这种通用解法,具体的题目见后方相关题目部分。
我们先来看下通用解法的解题思路,我画了一张图:
![backtrack](../assets/problems/backtrack.png)
通用写法的具体代码见下方代码区。
## 关键点解析
- 回溯法
- backtrack 解题公式
## 代码
* 语言支持JSC++Python3
JavaScript Code
```js
/*
* @lc app=leetcode id=90 lang=javascript
*
* [90] Subsets II
*
* https://leetcode.com/problems/subsets-ii/description/
*
* algorithms
* Medium (41.53%)
* Total Accepted: 197.1K
* Total Submissions: 469.1K
* Testcase Example: '[1,2,2]'
*
* Given a collection of integers that might contain duplicates, nums, return
* all possible subsets (the power set).
*
* Note: The solution set must not contain duplicate subsets.
*
* Example:
*
*
* Input: [1,2,2]
* Output:
* [
* [2],
* [1],
* [1,2,2],
* [2,2],
* [1,2],
* []
* ]
*
*
*/
function backtrack(list, tempList, nums, start) {
list.push([...tempList]);
for(let i = start; i < nums.length; i++) {
// 和78.subsets的区别在于这道题nums可以有重复
// 因此需要过滤这种情况
if (i > start && nums[i] === nums[i - 1]) continue;
tempList.push(nums[i]);
backtrack(list, tempList, nums, i + 1)
tempList.pop();
}
}
/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsetsWithDup = function(nums) {
const list = [];
backtrack(list, [], nums.sort((a, b) => a - b), 0, [])
return list;
};
```
C++ Code
```C++
class Solution {
private:
void subsetsWithDup(vector<int>& nums, size_t start, vector<int>& tmp, vector<vector<int>>& res) {
res.push_back(tmp);
for (auto i = start; i < nums.size(); ++i) {
if (i > start && nums[i] == nums[i - 1]) continue;
tmp.push_back(nums[i]);
subsetsWithDup(nums, i + 1, tmp, res);
tmp.pop_back();
}
}
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
auto tmp = vector<int>();
auto res = vector<vector<int>>();
sort(nums.begin(), nums.end());
subsetsWithDup(nums, 0, tmp, res);
return res;
}
};
```
Python Code:
```Python
class Solution:
def subsetsWithDup(self, nums: List[int], sorted: bool=False) -> List[List[int]]:
"""回溯法,通过排序参数避免重复排序"""
if not nums:
return [[]]
elif len(nums) == 1:
return [[], nums]
else:
# 先排序,以便去重
# 注意,这道题排序花的时间比较多
# 因此,增加一个参数,使后续过程不用重复排序,可以大幅提高时间效率
if not sorted:
nums.sort()
# 回溯法
pre_lists = self.subsetsWithDup(nums[:-1], sorted=True)
all_lists = [i+[nums[-1]] for i in pre_lists] + pre_lists
# 去重
result = []
for i in all_lists:
if i not in result:
result.append(i)
return result
```
## 相关题目
- [39.combination-sum](./39.combination-sum.md)
- [40.combination-sum-ii](./40.combination-sum-ii.md)
- [46.permutations](./46.permutations.md)
- [47.permutations-ii](./47.permutations-ii.md)
- [78.subsets](./78.subsets.md)
- [113.path-sum-ii](./113.path-sum-ii.md)
- [131.palindrome-partitioning](./131.palindrome-partitioning.md)