leetcode-90. 子集 II

题意:

    给出数组的子集(不能包含重复的元素)

思路:

    1. 如果数组不包含重复元素,那么dfs的简单回溯,还是很简单的,类似于

res = []
tmp = []
dfs(cur, nums[]):
   #不添加当前元素
   dfs(cur+1)
   
   #添加添加元素
   tmp.append(nums[cur])
   dfs(cur+1)
   tmp.remove(nums[cur])
   ···
   回溯的关键点,就是这里的remove,将现场清理干净
   这样的话,回到上一层的结点时,tmp才是正确的状态
   ···
     
    

2. 可是这里的元素是可重复的,所以思路可以转换一下,将每个位置设置成(key:value)的形式,也就是每个位置的选择是 [0, value]

tmp到当前位置后,添加元素是从无到该值的最大数量

class Solution {

    List<Integer> tmp;
    HashMap<Integer, Integer> dict;
    List<Integer> array;
    List<List<Integer>> res;

    void dfs(int cur, int n){
        if(cur == n){
            System.out.println(tmp);
            res.add(new ArrayList<Integer>(tmp));
            return;
        }
        for(int i=0;i<=dict.get(array.get(cur));++i){
            for(int j=1;j<=i;++j){
                tmp.add(array.get(cur));
            }
            dfs(cur+1, n);
            for(int j=1;j<=i;++j){
                tmp.remove(tmp.size()-1);
            }
        }
    }
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        tmp = new ArrayList<Integer>();
        dict = new HashMap<Integer, Integer>();
        int n = nums.length;
        for(int i=0;i<n;++i){
            dict.put(nums[i] , dict.getOrDefault(nums[i], 0) + 1); 
        }
        array = new ArrayList<Integer>();
        for(Integer i : dict.keySet()){
            array.add(i);
        }
        res = new ArrayList<List<Integer>>();
        dfs(0, array.size());
        return res;
    }
}

3. 但是,还有更骚的写法

第一种方法不行的原因在于,同样的数字,选了后者,没有选择前面的某一个的话,必然导致重复。

迭代时,若发现没有选择上一个数,且当前数字与上一个数相同,则可以跳过当前生成的子集。

这个条件,给回溯算法施加了约束,对于连续相同的数字,如果前面选了,后面才可以选,前面不选的话,后面也不可以选。

对于[2, 2, 2]来讲,只会出现下面的情况(1代表选择, 0代表不选择)

[0, 0, 0]

[1, 0, 0]

[1, 1, 0]

[1, 1, 1]

所以,约束限制了不会出现重复元素的情况。

class Solution {
    List<Integer> t = new ArrayList<Integer>();
    List<List<Integer>> ans = new ArrayList<List<Integer>>();

    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        dfs(false, 0, nums);
        return ans;
    }

    public void dfs(boolean choosePre, int cur, int[] nums) {
        if (cur == nums.length) {
            ans.add(new ArrayList<Integer>(t));
            return;
        }
        dfs(false, cur + 1, nums);
        if (!choosePre && cur > 0 && nums[cur - 1] == nums[cur]) {
            return;
        }
        t.add(nums[cur]);
        dfs(true, cur + 1, nums);
        t.remove(t.size() - 1);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值