递归与回溯10:子集,有重复元素

Posted 纵横千里,捭阖四方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了递归与回溯10:子集,有重复元素相关的知识,希望对你有一定的参考价值。

这个题与前面的相比又加码了,如果给定的子集有重复元素该怎么办?先看题目要求:LeetCode90给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。示例:

输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

这个问题的关键是怎么处理去重的问题,这里卡哥的解析挺好,我们直接节选了:

抽象为树形结构,那么“使用过”在这个树形结构上是有两个维度的,一个维度是同一树枝上使用过,一个维度是同一树层上使用过。没有理解这两个层面上的“使用过” 是造成大家没有彻底理解去重的根本原因。

那么我们是要同一树层上使用过,还是统一树枝上使用过呢?

回看一下题目,元素在同一个组合内是可以重复的,怎么重复都没事,但两个组合不能相同。

所以我们要去重的是同一树层上的“使用过”,同一树枝上的都是一个组合里的元素,不用去重。

后面要学习的排列问题里去重也是这个套路,所以理解“树层去重”和“树枝去重”非常重要。

去重时我们一般要先对元素进行排序,这样会容易很多。

 从图中可以看出,同一树层上重复取2 就要过滤掉,同一树枝上就可以重复取2,因为同一树枝上元素的集合才是唯一子集!

实现代码:

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;
        
    

以上是关于递归与回溯10:子集,有重复元素的主要内容,如果未能解决你的问题,请参考以下文章

递归与回溯11:子集问题,递增子序列

求子集 递归加回溯

一文通数据结构与算法之——回溯算法+常见题型与解题策略+Leetcode经典题

leetcode-78. 子集

回溯-递归练习题集

LeetCode78:子集