90. 子集 II

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

解题思路:

解法一:枚举所有的子集后(重复的子集也记录),去重

递归枚举所有的子集,因为数组中可能包含重复的元素,也就是子集会出现重复的情况,在拿到所有的子集时,对子集进行排序,然后将排序后的子集添加到set中去重。

具体算法如下:

  1. 初始变量:
    1. tem:保存nums的一个子集
    2. result:保存所有的子集
  2. 定义递归函数,process(int nums,int cur):
    1. 在递归函数中选择将nums[cur]加入子集中或者不加入子集中
  3. 递归终止的条件,如果cur >= nums.length,return
  4. 否则:
    1. tem.add(nums[cur]):将nums[cur]加入子集中
    2. result.add(new ArrayList<>(tem)):tem此时保存的就是一个子集,加入到result中
    3. process(nums,cur+1):选择下一个加入子集中的元素
    4. 回溯:
      1. tem.remove(tem.size()-1):将nums[cur]从子集中移除,也就是不选择nums[cur]
      2. process(nums,cur+1)
  5. 递归结束后,result中保存的就是所有的子集,但是因为数组中元素会出现重复的情况,需要去重,所有可以对result中每个子集排序下,然后将排序后的子集加入set中,这样,set最终保存的就是不重复的子集

AC代码:

class Solution {
    static List<Integer> tem;
    static Set<List<Integer>> result;
    public static List<List<Integer>> subsetsWithDup(int[] nums) {
        tem = new ArrayList<>();
        result = new HashSet<>();
        result.add(new ArrayList<>());
        process(nums, 0);
        Set<List<Integer>> tem = new HashSet<>();
        //排序去重
        result.forEach(value->{
            Collections.sort(value);
            tem.add(value);
        });
        return new ArrayList<>(tem);
    }

    public static void process(int[] nums, int index) {
        if (index >= nums.length) {
            return;
        }
        tem.add(nums[index]);
        result.add(new ArrayList<>(tem));
        process(nums, index + 1);
        tem.remove(tem.size() - 1);
        process(nums, index + 1);
    }
}

解法二:在解法一中,递归算法,会将重复的子集也枚举出来,最后需要对得到的结果去重,效率比较低,可以直接在递归枚举过程中跳过重复的子集,提升效率。具体算法如下:

  1. 对于当前选择的数nums[index],如果前面有与nums[index]相同的数 nums[i] (i<index),并且nums[i] 没有选,那么就可以跳过 nums[index](不选这个元素)。
    1. 比如[1,4,4],是否选第一个4,
      1. 当选择这个4的时候,所有可以确定的子集为[ [],[1], [1,4], [4] ],
      2. 当不选这个4时,所有子集为[ [],[1] ]
    2. 当需要选择第二个4时,[1,4,4]:是否选第二个4
      1. 如果第一个4没有选,那么第二个4也不选,因为(第一个4不选,第2个4选),这种情况和 (第1个4选,第2个4不选)这种情况的子集相同,所以可以直接第2个4
      2. 如果第1个4选,那么第2个4可以选,也可以不选,选的话可以构成更长的一个子集
  2. 所以,可以先对nums数组进行排序,在递归过程中,对于当前选择的数nums[index],如果前面有与nums[index]相同的数 nums[i] (i<index),并且nums[i] 没有选,那么nums[index]就不用选。这样就不会包含重复的子集了

AC代码

class Solution {
    static List<Integer> tem;
    static Set<List<Integer>> result;
    public static List<List<Integer>> subsetsWithDup(int[] nums) {
        tem = new ArrayList<>();
        result = new HashSet<>();
        result.add(new ArrayList<>());

        Arrays.sort(nums);
        process(nums, 0);
        return new ArrayList<>(result);
    }

    public static void process(int[] nums, int index) {
        if (index >= nums.length) {
            return;
        }

        //跳过这个数
        if (index>0&&nums[index-1]==nums[index]&&!tem.contains(nums[index])){
            process(nums,index+1);
            return;
        }

        tem.add(nums[index]);
        result.add(new ArrayList<>(tem));
        process(nums, index + 1);
        tem.remove(tem.size() - 1);
        process(nums, index + 1);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值