90. 子集 II

69 篇文章 1 订阅
63 篇文章 0 订阅

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

说明:解集不能包含重复的子集。
示例:

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

(1)遍历添加法,参考子集
思路:每一个新子集都是当前已有子集加上当前遍历数字即可,因为有重复所以先对数组排序,并且对链表添加进行去重处理;
弊端:每一次都要判断是否重复,很费时间;

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        //建立结果集并且添加空集
        List<List<Integer>> res = new ArrayList<>();
        res.add(new ArrayList<>());
        //对数组进行排序
        Arrays.sort(nums);
        sub1(nums, res);
        return res;
    }
    //遍历法
    private void sub1(int[] nums, List<List<Integer>> res) {
        //每次都在已有子集的基础上添加当前遍历的元素,然后生成新的子集
        for (int n : nums) {
            //记录当前的子集个数
            int sumSub = res.size();
            //拿出当前的子集个数,加上n生成新子集并加入
            for (int i = 0; i < sumSub; i ++) {
                //add()方法返回的Boolean值
                List<Integer> sub = new ArrayList(res.get(i));//根据当前子集建立新的子集
                sub.add(n);
                //不能重复
                if (!res.contains(sub))
                    res.add(sub);
            }
        }
    }
}

(2)动态规划,对上面算法进行改进;
思路:其实就是对重复的处理;遍历分为两种情况:一,没有重复,那么就从结果集的所有子集加上该元素num[i],并且记录没添加以前的结果集子集个数;二,有重复,那么就在添加num[i-1]后的个数减去没添加以前的个数,也就添加num[i-1]后形成的这些新子集上面添加这个重复元素即可,并且记录没添加当前这个数之前的结果集子集个数,再添加完之后更新记录;
注意:说起来比较绕口,看一下代码会明白滴!

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        //建立结果集并且添加空集
        List<List<Integer>> res = new ArrayList<>();
        res.add(new ArrayList<>());
        //对数组进行排序
        Arrays.sort(nums);
        sub(nums, res);
        return res;
    }
    //每次都要判断是否已经存在,耗时间
    private void sub(int[] nums, List<List<Integer>> res) {
        if (nums == null || nums.length == 0)
            return;
        res.add(Arrays.asList(nums[0]));
        //记录每次遍历时res的子集个数(也就是没添加新子集时的长度)
        int index = 1;
        //没重复则全部添加子集然后生成新子集
        for (int i = 1; i < nums.length; i ++) {
             if (nums[i] != nums[i-1]) {            
                 //记录当前的子集个数(更新)           
                 index = res.size();         
                 //拿出当前的子集个数,加上n生成新子集并加入     
                 for (int j = 0; j < index; j ++) {         
                     //add()方法返回的Boolean值           
                     List<Integer> sub = new ArrayList(res.get(j));//根据当前子集建立新的子集              
                     sub.add(nums[i]);              
                     res.add(sub);             
                 }        
             } else {//相等则从遍历nums[i-1]位置开始遍历res然后生成新的子集                
                 int temp = res.size();                
                 for (int j = index; j < temp; j ++) {    
                     List<Integer> sub = new ArrayList(res.get(j));//根据当前子集建立新的子集
                     sub.add(nums[i]);             
                     res.add(sub);
                 }
                 //更新
                 index = temp;
             }
        }
    }
}

(3)递归回溯
思路:就是一句话,递归时挨个添加,回溯时看这个元素与其前一个元素是否相等;

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        //建立结果集并且添加空集
        List<List<Integer>> res = new ArrayList<>();
        res.add(new ArrayList<>());
        //对数组进行排序
        Arrays.sort(nums);
        subset(nums,0,new ArrayList<>(),res,new boolean[nums.length]);
        return res; 
    //递归回溯
    private void subset(int[] nums, int start, List<Integer> sub, List<List<Integer>> res, boolean[] v) {
        //遍历数组,
        for (int i = start; i < nums.length; i ++) {
            if (!v[i] && (i == 0 || nums[i] != nums[i-1] || v[i-1])) {//要加上重复的判断(首次看最后条件,回溯看中间条件)
                sub.add(nums[i]);//子集添加新元素
                res.add(new ArrayList<>(sub));//生成新子集添加到结果集中
                v[i] = true;//递归时挨个添加
                subset(nums,i+1,sub,res,v);//递归
                v[i] = false;//回溯
                sub.remove(sub.size()-1);//回溯时删除子集最后一个元素
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值