力扣第九十题-子集 II

303 阅读3分钟

「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

前言

力扣第九十题 子集 II 如下所示:

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

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

示例 1:

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

示例 2:

输入: nums = [0]
输出: [[],[0]]

一、思路

这一题需要注意的有两点:

  • 数组 nums 中可能有重复的元素
  • 子集中的结果不能重复

这一题毋庸置疑是要使用递归来实现的,但是为了让我们在处理重复元素时更方便,我们第一步先将 nums 按照升序排列。

这样在递归的过程中,如果碰到 nums[i]nums[i-1] 时我们跳过即可。要做到在子集中的结果不重复,我们只需要保证一点即可:同一层递归的处理中,保证每一次取的元素都是不同的

需要注意的是,当 nums = [1, 2, 2] 时,我们肯定有一层递归是为了取子集大小为 2 的结果。
当我们第一个元素取得 nums[1] 值为 2 时,下一层递归会从 nums[2] 开始取。
虽然此时 nums[2] == nums[1] 当时因为我们在当前层中是第一次取 2,所以对于 [2, 2] 这个结果我们不能跳过

为了保证当前层递归中取的值都是不同的,我们可以采取以下两个措施

  1. 使用布尔数组 visited[] 表示当前层的第 i 个元素是否访问过
  2. 下一次递归取元素的起始位置为 i+1

综上所述,大致的步骤如下所示:

  1. 因知道递归取的子集长度的范围为 0 ~ len,所以我们可以在主函数中枚举 1 ~ len-1 长度。(0 一定为空集,len 一定为全集)
  2. 递归的方法就是获取所有长度为 x (0 < x < len) 的子集(这样在添加结果时比较方便,只要当前 list.size = x 即可)

tips:在返回结果集前,需要加上空集和全集,因为我们在主函数中是从 1 ~ len-1 的。

二、实现

实现代码

实现代码与思路中保持一直

    List<List<Integer>> ret = new ArrayList<>();

    public List<List<Integer>> subsetsWithDup(int[] nums) {
        // 将nums升序排列
        Arrays.sort(nums);
        int len = nums.length;
        ret.add(new ArrayList<>());
        for (int i=1; i<len; i++) {
            dfs(nums, 0, i, new boolean[len], new ArrayList<>());
        }
        ret.add(Arrays.stream(nums).boxed().collect(Collectors.toList()));
        return ret;
    }

    public void dfs(int[] nums, int begin, int len, boolean[] visited, List<Integer> list) {
        if(list.size() == len) {
            ret.add(new ArrayList<>(list));
            return;
        }
        for (int i=begin; i<nums.length; i++) {
            // 剪枝
            if (i > 0 && nums[i] == nums[i-1] && !visited[i-1])
                continue;
            list.add(nums[i]);
            visited[i] = true;
            dfs(nums, i+1, len, visited, list);
            visited[i] = false;
            list.remove(list.size()-1);
        }
    }

测试代码

    public static void main(String[] args) {
        int[] nums = {1,2,2, 2};
        new Number90().subsetsWithDup(nums);
    }

结果

image.png

三、总结

这个击败率还不是很理想,仍有优化的空间。

感谢看到最后,非常荣幸能够帮助到你~♥

如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~