集合求子集系列

78. 子集

力扣链接

思路:

做法和组合的题目类似,差别在于多了不同长度的子集,对于不同长度的子集,可分别通过多次调用递归,也就是遍历长度为1,2,3,4等不同长度的子集。其中顺序无关这个其实就要求在递归过程中,防止重复遍历,具体解析见递归函数。

 private List<List<Integer>> res = new ArrayList<>();

 public List<List<Integer>> subsets(int[] nums) {        
     //遍历长度为0、1、2、3...的子集
     int len = nums.length;
     boolean[] hash = null;
     int[] tmp = null;
     for(int l = 0 ; l <= len; l++) {
         hash = new boolean[len];
         tmp = new int[l];
         digui(nums,hash,tmp,0,l,-1); //注意prevIndex得小于0
     }
     return res;
 }

由于子集元素是不能重复的,所以需要防止重复遍历,例如123,321是一样的子集。我们以[1,2,3]为例,长度为2的子集可以为12,13,23,而我们想避免全排列的21,31,32,观察12,13,23这些子集,它们的特点都是后一个数的下标比前一个大递归时。因此递全排列时,只需保证当前元素的下标大于前一个元素的下标(prevIndex作用),这里不能用元素的值,因为nums数组不一定有序,例如[3,2,1]。其处理做法跟组合是一样的。

//index代表第几层,len代表子集长度,prevIndex代表上一个元素的下标
public void digui(int[] nums, boolean[] hash, int[] tmp, int index, int len, int prevIndex){
     if(index == len) {
         List<Integer> list = new ArrayList<>();
         for(Integer a : tmp) {
             list.add(a);
         }
         res.add(list);
         return;
     }
     int l = nums.length;
     for(int i=0; i < l; i++) {
         //防止重复遍历
         if(i > prevIndex) {
             if(hash[i] == false) {
                 tmp[index] = nums[i];                
                 hash[i] = true;
                 digui(nums, hash, tmp, index+1, len, i);
                 hash[i] = false;
             }                            
         }
     }
 }

90. 子集 II

https://leetcode.cn/problems/subsets-ii/
包含重复的集合,求子集

class Solution {
    private List<List<Integer>> res;
    private List<Integer> zj; //存放子集
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        res = new ArrayList<>();        
        res.add(new ArrayList<>());
        int l = nums.length;
        zj = new ArrayList<>(l);
        //排序
        Arrays.sort(nums);
        for(int len = 1; len <= l; len++) {
            dfs(len, nums, 0);
        }
        return res;
    }

    public void dfs(int len, int[] nums, int prev) {
        if(len == 0) {
            //注意需要new
            res.add(new ArrayList<>(zj));
            return;
        }
        int l = nums.length;
        for(int i = prev; i < l; i++) {
            //避免重复访问
            if(i > prev && nums[i] == nums[i-1]) continue;
            zj.add(nums[i]);
            dfs(len - 1, nums, i + 1);
            zj.remove(zj.size() - 1);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值