78. Subsets I & II

QUESTION

Given a set of distinct integers, nums, return all possible subsets.

Note: The solution set must not contain duplicate subsets.

For example,

If nums = [1,2,3], a solution is:

[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]
THOUGHT I

利用位操作,很好理解,当某一位为1的时候代表可以加入到集合当中去。对于数组[1,2,3],可以用一个下标0和1表示是否选择该数字,0表示未选择,1表示选中,那么每一组3个0和1的组合表示一种选择,3位共有8种选择,分别是:

000 对应[] 
001 对应[3] 
010 对应[2] 
011 对应[2,3] 
100 … 
101 
110 
111

那么上面为1的位表示数组中该位被选中。 那么只需要遍历0到1<< length中的数,判断每一个数中有那几位为1,为1的那几位即会构成一个子集中的一个元素。

CODE
public class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        ArrayList<List<Integer>> result = new ArrayList<List<Integer>>();

        for(int i = 0;i < 1 << nums.length;i++){
            ArrayList<Integer> temp = new ArrayList<Integer>();
            for(int j = 0;j < nums.length;j++){
                if((i & (1 << j)) != 0 )
                    temp.add(nums[j]);
            }
            result.add(new ArrayList(temp));
        }
        return result;
    }
}
RESULT

runtime complexity is O(2^n),space complexity is O(1);

SPECIAL ATTENTION
  • == 还有!= 的优先级要高于&;
== has higher priority than &. You might want to wrap your operations in () to specify your own priority.
((a[0] & 1) == 0)
Similarly for all parts of the if condition.
  • 把一个List加入到另一个List当中去,如果List发生变化,存入的结果也会发生变化,也就是传址引用,我们的做法是new一个新的List;
  • 注意阶段性空间的初始化位置问题;
THOUGHT II

基本思路循环+dfs,生成指定元素数目(0,1,2,…array.size()个元素)的组合。

CODE
public class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        ArrayList<List<Integer>> result = new ArrayList<List<Integer>>();
        ArrayList<Integer> temp = new ArrayList<Integer>();
        result.add(temp);
        if(nums.length == 0)
            return result;
        for(int i = 1;i <= nums.length;i++){
            dfs(result,temp,nums,i,0);
        }
        return result;
    }
    public void dfs(ArrayList<List<Integer>> result,ArrayList<Integer> temp,int[] nums,int len,int start){
        if(temp.size() == len){//边界条件
            result.add(new ArrayList<Integer>(temp));
            return;
        }
        for(int i = start;i < nums.length;i++){
            temp.add(nums[i]);
            dfs(result,temp,nums,len,i+1);
            temp.remove(temp.size() - 1);
        }
    }
}
RESULT

递归类的时间复杂度不会算;

THOUGHT III 二刷

这里有一个通用的解法,适用于回溯问题A general approach to backtracking questions in Java (Subsets, Permutations, Combination Sum, Palindrome Partitioning)

CODE
public class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        if(nums == null || nums.length == 0)
            return res;
        Arrays.sort(nums);
        List<Integer> tempList = new ArrayList<>();
        helper(res,tempList,nums,0);
        return res;
    }
    private void helper(List<List<Integer>> res,List<Integer> tempList,int[] nums,int start){
        res.add(new ArrayList<>(tempList));
        for(int i = start;i < nums.length;i++){
            tempList.add(nums[i]);
            helper(res,tempList,nums,i + 1);
            tempList.remove(tempList.size() - 1);
        }
    }
}
QUESTION II

Given a collection of integers that might contain duplicates, nums, return all possible subsets.

Note: The solution set must not contain duplicate subsets.

For example,

If nums = [1,2,2], a solution is:

[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]
THOUGHTS

比上个解法多了一个排序,多了一个去重。这里不是很理解为什么不排序不能通过。没有要求从小到大输出啊。

CODE
public class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {

        ArrayList<List<Integer>> result = new ArrayList<List<Integer>>();
        ArrayList<Integer> temp = new ArrayList<Integer>();
        result.add(temp);
        if(nums.length == 0)
            return result;
        Arrays.sort(nums);
        for(int i = 1;i <= nums.length;i++){
            dfs(result,temp,nums,i,0);
        }
        return result;
    }
    public void dfs(ArrayList<List<Integer>> result,ArrayList<Integer> temp,int[] nums,int len,int start){
        if(temp.size() == len){//边界条件
            result.add(new ArrayList<Integer>(temp));
            return;
        }
        for(int i = start;i < nums.length;i++){
            temp.add(nums[i]);
            dfs(result,temp,nums,len,i+1);
            temp.remove(temp.size() - 1);
            while(i<(nums.length - 1)&&nums[i] == nums[i+1]) {  
                i++;  
            }  
        }
    }

}
THOUGHT II

用回溯法来进行遍历,别忘了要先排序

CODE
public class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        if(nums == null || nums.length == 0)
            return res;
        Arrays.sort(nums);
        List<Integer> temp = new ArrayList<>();
        helper(res,temp,nums,0);
        return res;
    }
    private void helper(List<List<Integer>> res,List<Integer> temp,int[] nums,int start){
        res.add(new ArrayList<>(temp));
        for(int i = start;i < nums.length;i++){
            if(i > start && nums[i] == nums[i - 1]) 
                continue;
            temp.add(nums[i]);
            helper(res,temp,nums,i + 1);
            temp.remove(temp.size() - 1);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值