leetcode78子集

题目描述

在这里插入图片描述

想法

由于数组元素是不重复的,假设数组长度为n,那么子集总数应该是 2 n 2^{n} 2n个,且子集的大小为0,1,2…n个,数量分别为Cin,i为子集大小,我们可以采用减治的方法来解决这个问题,以一个普通的输入为例
输入nums = {x1,x2,x3.....xn-1,xn}输入大小为n,
那么这个输入数组的所有子集可以分成
nums1 = {x1,x2,x3....xn-1}的所有子集 再加上 它的所有子集中再添加上xn这个元素之后的结果

将问题规模不断缩减,直到待求的数组的长度为0时,它的子集为[]

递归

上面的想法可以使用递归的方式实现,具体为:

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        return getSubsets(nums,nums.length-1);
    }

    public List<List<Integer>> getSubsets(int[] nums, int end) {
        List<List<Integer>> res = null;
        if(end < 0) {
            res = new ArrayList<>();
            res.add(new ArrayList<Integer>());
        }else{
            List<List<Integer>> son = getSubsets(nums, end-1);
            int size = son.size();
            for(int i = 0; i < size; ++i) {
                List<Integer> temp = new ArrayList<>(son.get(i));
                temp.add(nums[end]);
                son.add(temp);     
            }
            res = son;
        }
        return res;
    }
}
简化

上述代码从输入数组尾部开始递归,但实际上从计算出最小子问题的结果时才会计算更长的,所以也可以简化为:

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        res.add(new ArrayList<Integer>());
        for(int num: nums) {
            int size = res.size();
            for(int i = 0; i < size; ++i) {
                List<Integer> temp = new ArrayList(res.get(i));
                temp.add(num);
                res.add(temp);
            }
        }
        return res;
    }
}

思路其实是一致的,两种方法都需要注意,再遍历子问题的结果集时,必须先获取大小再利用下标进行访问,不能使用迭代器或foreach循环,原因是我们在访问时对这个结果集进行了更新,会触发ArrayList的快速失败机制而使得程序不正常退出。

两种方法的时间复杂度均为 O ( N 2 N ) O(N2^{N}) O(N2N),可以通过 T ( N ) = T ( N − 1 ) + 2 N − 1 T(N)=T(N-1)+2^{N-1} T(N)=T(N1)+2N1求解。

新的思路

在看别人的解法时,发现一种很有趣的思路,即采用掩码,如图:
在这里插入图片描述
即创建一个与输入数组同样长度的二进制数作为掩码,当他在全0~全1范围内变化的时候,就可以得到它的唯一子集。

输入数组为N位,二进制数变化范围为 2 N 2^{N} 2N,每一次变化都需要遍历一次数组从掩码为1的位置获取子集,长度为N,所以时间复杂度为 O ( N 2 N ) O(N2^{N}) O(N2N)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值