Leetcode 78 subset|数组子集

描述:给一个没有重复元素的数组,返回其所有子数组构成的数组

    Input:[1,2,3]
    
    Output:[[],[1],[1,2],[1,3],[1,2,3],[2],[2,3],[3]]

解法一:递归,DFS;在一个可能的子数组中,源数组的每个元素要嘛存在,要嘛不存在,两种状态,和二叉树正好吻合,如下图的二叉树(满二叉树),通过深度优先搜索,所有叶子节点的值就是所有可能的子集,所以递归深度到达叶子时就return

 

代码:

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result = new ArrayList();
        List<Integer> temp = new ArrayList();
        generate(0,nums,result,temp);
        return result;
    }
    public void generate(int deepth, int[] nums, 
                         List<List<Integer>> result, List<Integer> temp){
        if(deepth==nums.length){
            result.add(temp);
            return;
        }
        //源数组第i元素不在子集中
        generate(deepth+1, nums, result, new ArrayList<>(temp));
        temp.add(nums[deepth]);
        //源数组第i元素在子集中
        generate(deepth+1, nums, result, new ArrayList<>(temp));
    }
}

解法二:递归+循环;记nums[i:]为nums从第i个元素到最后一个元素的部分,nums[1:0]的所有子集结果为tempRes,nums[0:]的所有子集结果=tempRes的每一个元素前面加上nums[0]所构成的结果tempRes0+tempRes

代码:

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        return generate(nums,0);
    }
    public List<List<Integer>> generate(int[] nums, int start){
        List<List<Integer>> result = new ArrayList();
        if(start == nums.length-1){
            List<Integer> list1 = new ArrayList();
            List<Integer> list2 = new ArrayList();
            list2.add(nums[nums.length-1]);
            result.add(list1);
            result.add(list2);
            return result;
        }
        List<List<Integer>> temp1 = generate(nums, start+1);
        for(List<Integer> l:temp1){
            //新复制的数组
            List<Integer> tempList = new ArrayList();
            for(int i:l){
                tempList.add(i);
            }
            tempList.add(nums[start]);
            result.add(tempList);
            result.add(l);
        }
        return result;
    }
}

解法三:递归+循环;回溯的思想,也是将生成子集的过程理解为一个二叉树

(1)外层循环逐一往中间集合 temp 中加入元素 nums[i],使这个元素处于存在状态

 

(2)开始递归,递归中携带加入新元素的 temp,并且下一次循环的起始是 i 元素的下一个,因而递归中更新 i 值为 i + 1

 

(3)将这个从中间集合 temp 中移除,使该元素处于不存在状态

代码:

    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> list = new ArrayList();
        Arrays.sort(nums);
        backtrack(list, new ArrayList(),nums,0);
        return list;
    }
    public void backtrack(List<List<Integer>> list,List<Integer> tempList, int[] nums,int start){
        list.add(new ArrayList<>(tempList));
        for(int i=start;i<nums.length;i++){
            tempList.add(nums[i]);
            backtrack(list, tempList, nums, i+1);
            tempList.remove(tempList.size()-1);
        }
    }

 

思考 :这里的递归+循环也可以遍历完所有的子集生成情况,这里和第一种解法不同,第一种解法是当递归深度达到叶子节点时才向结果集res中加入temp,这里第三种解法是,在本节点就将本节点处(原数组遍历到第i位)temp的值先加入,然后将子集中有nums[i+1]的所有子集加入res,和子集中没有nums[i+1]的所有子集加入res

解法四:位运算,源数组有n个元素,子集就有2^n个,将源数组的每个元素也用n为二进制来表示,所有子集的可能取值也用一个n位二进制来表示

比如

    Input:[1,2,3]
    分析:
        用A、B、C代表源数组的0、1、2位元素
        令A=001;B=010;C=100
        那么子集为001表示只有C,101表示有A和C
        所有子集的大小在0到2^3-1=7之间,也是0到1<<3-1之间
        每个子集的可能取值&1<i(i为当前元素在源数组的位置)的结果等于1<i的话说明第i个元素在这个子集中,就将nums[i]加入temp中。

 代码:

    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList();
        int allSets = 1<<nums.length;//最大结果值+1
        for(int i=0;i<allSets;i++){//遍历所有可能的结果值
            List<Integer> temp = new ArrayList();
            //遍历nums集合中,看每个元素是否应该加入
            for(int j=0;j<nums.length;j++){
                if((i&(1<<j))==(1<<j)){
                    temp.add(nums[j]);
                }
            }
            res.add(temp);
        }
        return res;
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值