子集问题+去重

创新互联专业为企业提供昔阳网站建设、昔阳做网站、昔阳网站设计、昔阳网站制作等企业网站建设、网页设计与制作、昔阳企业网站模板建站服务,10多年昔阳做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。
力扣题目链接:https://leetcode-cn.com/problems/subsets-ii/
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
做本题之前一定要先做78.子集。
这道题目和78.子集区别就是集合里有重复元素了,而且求取的子集要去重。
那么关于回溯算法中的去重问题,在40.组合总和II中已经详细讲解过了,和本题是一个套路。
剧透一下,后期要讲解的排列问题里去重也是这个套路,所以理解“树层去重”和“树枝去重”非常重要。
用示例中的[1, 2, 2] 来举例,如图所示:(注意去重需要先对集合排序)
子集II
从图中可以看出,同一树层上重复取2 就要过滤掉,同一树枝上就可以重复取2,因为同一树枝上元素的集合才是唯一子集!
本题就是其实就是78.子集的基础上加上了去重,去重我们在40.组合总和II也讲过了,所以我就直接给出代码了:
C++代码如下:
- class Solution {
 - private:
 - vector
 > result; - vector
 path; - void backtracking(vector
 & nums, int startIndex, vector & 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
 > subsetsWithDup(vector & nums) { - result.clear();
 - path.clear();
 - vector
 used(nums.size(), false); - sort(nums.begin(), nums.end()); // 去重需要排序
 - backtracking(nums, 0, used);
 - return result;
 - }
 - };
 
使用set去重的版本。
- class Solution {
 - private:
 - vector
 > result; - vector
 path; - void backtracking(vector
 & nums, int startIndex, vector & used) { - result.push_back(path);
 - unordered_set
 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
 > subsetsWithDup(vector & nums) { - result.clear();
 - path.clear();
 - vector
 used(nums.size(), false); - sort(nums.begin(), nums.end()); // 去重需要排序
 - backtracking(nums, 0, used);
 - return result;
 - }
 - };
 
本题也可以不适用used数组来去重,因为递归的时候下一个startIndex是i+1而不是0。
如果要是全排列的话,每次要从0开始遍历,为了跳过已入栈的元素,需要使用used。
代码如下:
- class Solution {
 - private:
 - vector
 > result; - vector
 path; - void backtracking(vector
 & 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
 > subsetsWithDup(vector & 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
- class Solution {
 - List
 > result = new ArrayList<>();// 存放符合条件结果的集合
- LinkedList
 path = new LinkedList<>();// 用来存放符合条件结果 - boolean[] used;
 - public List
 > 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
- class Solution:
 - def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
 - res = [] #存放符合条件结果的集合
 - path = [] #用来存放符合条件结果
 - def backtrack(nums,startIndex):
 - res.append(path[:])
 - for i in range(startIndex,len(nums)):
 - if i > startIndex and nums[i] == nums[i - 1]: #我们要对同一树层使用过的元素进行跳过
 - continue
 - path.append(nums[i])
 - backtrack(nums,i+1) #递归
 - path.pop() #回溯
 - nums = sorted(nums) #去重需要排序
 - backtrack(nums,0)
 - return res
 
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
 - if i>start&&num[i]==num[i-1]{
 - continue
 - }
 - temp=append(temp,num[i])
 - dfs(temp,num,i+1)
 - temp=temp[:len(temp)-1]
 - }
 - }
 
Javascript
- var subsetsWithDup = function(nums) {
 - let result = []
 - let path = []
 - let sortNums = nums.sort((a, b) => {
 - return a - b
 - })
 - function backtracing(startIndex, sortNums) {
 - result.push(path.slice(0))
 - if(startIndex > nums.length - 1) {
 - return
 - }
 - for(let i = startIndex; i < nums.length; i++) {
 - if(i > startIndex && nums[i] === nums[i - 1]) {
 - continue
 - }
 - path.push(nums[i])
 - backtracing(i + 1, sortNums)
 - path.pop()
 - }
 - }
 - backtracing(0, sortNums)
 - return result
 - };
 
                文章标题:你知道吗?子集问题也要去重了!
                
                本文地址:http://www.csdahua.cn/qtweb/news26/254176.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网