78、90、子集

  1. 78、子集
    1. 示例:
  • 题解
    1. 1、递归回溯
    2. 2、迭代法
  • 90 子集II
    1. 示例:
  • 题解
    1. 1、递归回溯——深度优先搜索
    2. 2、迭代法
    3. 3、利用位操作
  • 78、子集

    给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

    说明:解集不能包含重复的子集。

    示例:

    输入: nums = [1,2,3]
    输出:
    [
      [3],
      [1],
      [2],
      [1,2,3],
      [1,3],
      [2,3],
      [1,2],
      []
    ]
    

    题解

    1、递归回溯

    深度优先搜素

    class Solution {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        public List<List<Integer>> subsets(int[] nums) {
            //2^nums.length 个集合
            subsets(nums,0,new LinkedList<Integer>());
            return res;
        }
        
        private void subsets(int[] nums, int index,LinkedList<Integer> temp){
            res.add(new LinkedList<Integer>(temp));
            for (int i = index; i < nums.length;++i) {
                temp.add(nums[i]);
                subsets(nums,i+1,temp);
                temp.removeLast();
            }
        }
    }
    

    2、迭代法

    class Solution {
        public List<List<Integer>> subsets(int[] nums) {
            // 结果
            List<List<Integer>> list = new ArrayList<>();
            // 特殊case
            if(nums.length == 0) return list;case
            // 空集也是子集
            list.add(new ArrayList<>());
            for(int i = 0;i < nums.length;i++){
                int size = list.size();
                List<Integer> list2 = null;
                // 遍历已有的结果
                for(int j = 0;j < size;j++){
                    list2 = new ArrayList<>();
                    // 复制一个已有的子集
                    list2.addAll(list.get(j));
                    // 加入新的元素成为新的子集
                    list2.add(nums[i]);
                    // 加入新的子集
                    list.add(list2);
                }
            }
            return list;
        }
    }
    

    90 子集II

    给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

    说明:解集不能包含重复的子集。

    示例:

    输入: [1,2,2]
    输出:
    [
      [2],
      [1],
      [1,2,2],
      [2,2],
      [1,2],
      []
    ]
    

    链接:https://leetcode-cn.com/problems/subsets-ii

    题解

    1、递归回溯——深度优先搜索

    class Solution {
        public List<List<Integer>> subsetsWithDup(int[] nums) {
            // 回溯法
            List<List<Integer>> res = new ArrayList<List<Integer>>();
            // 排序方便剪枝
            Arrays.sort(nums);
            subsetsWithDupCore(nums,0,new ArrayList<>(),res);
            return res;
        }
    
        private void subsetsWithDupCore(int[] nums,int index,List<Integer> temp,List<List<Integer>> res) {
            // 新增一个结果
            res.add(new ArrayList<Integer>(temp));
            // 遍历所有剩余结点
            for (int i = index;i < nums.length;i++) {
                // 剪枝判断
                if (i > index && nums[i] == nums[i-1]) {
                    continue;
                }
                temp.add(nums[i]);
                // 递归, 索引用i
                subsetsWithDupCore(nums,i+1,temp,res);
                // 移除元素进行回溯
                temp.remove(temp.size() - 1);
            }
        }
    
    }
    

    2、迭代法

    利用旧的解去生成新的解,去除重复的情况。

    • 分析迭代过程去除重复解


    我们看到第 4 行黑色的部分,重复了,是怎么造成的呢?

    第 4 行新添加的 2 要加到第 3 行的所有解中,而第 3 行的一部分解是旧解,一部分是新解。可以看到,我们黑色部分是由第 3 行的旧解产生的,橙色部分是由新解产生的。

    而第 1 行到第 2 行,已经在旧解中加入了 2 产生了第 2 行的橙色部分,所以这里如果再在旧解中加 2 产生黑色部分就造成了重复。

    所以当有重复数字的时候,我们只考虑上一步的新解,算法中用一个指针保存每一步的新解开始的位置即可。

    作者:windliang
    链接:https://leetcode-cn.com/problems/subsets-ii/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-19/

    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> res = new ArratList<List<Integer>>();
        Arrays.sort(nums);
        int index = 1;// 保存新解的位置
        for (int i = 0;i < nums.length;i++) {
            List<List<integer>> res_temp = new ArrayList<List<Integer>>();
            for (int j = 0;j < res.size();j++) {
                // remove dup temp cases
                // i > 0 && nums[i] == nums[i-1] 表示有重复数字
                // j < start 表示要当前位置不是上一次新解开始的位置
                if (i > 0 && nums[i] == nums[i-1] && j < start) {
                    continue;
                }
                List<Integer> temp = new ArrayList<>(res.get(j));
                temp.add(nums[i]);
                res_temp.add(temp);
            }
            // 更新新解的开始位置
            start = res.size();
            res.addAll(res_temp);
        }
        return res;
    }
    
    • 统计当前的重复元素个数来去除重复解
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        List<Integer> empty = new ArrayList<>();
        res.add(empty);
        Arrays.sort(nums);
        for (int i = 0;i < nums.length;i++) {
            // 统计数值为nums[i]重复元素个数
            int dupCount= 0;
            while((i+1) < nums.length && nums[i+1] == nums[i]) {
                dupCount++;
                i++;//  下标后移,跳过重复元素
            }
            int preNum =  res.size();
            for(int j = 0;j < preNum;j++) {
                List<Integer> temp = new ArrayList<>(res.get(j));
                // 逐个添加重复元素
                for (int k = 0;k < dupCount;k++) {
                    temp.add(nums[i]);
                    res.add(temp);
                }
            }
        }
        return res;
    }
    

    3、利用位操作

    数组元素过多时,会超时。

    class Solution{
        public List<List<Integer>> subsetsWithDup(int[] num) {
            Arrays.sort(num);
            List<List<Integer>> lists = new ArrayList<>();
            int subsetNum = 1<<num.length;
            // 遍历所有的位
            for(int i = 0;i < subsetNum;i++){
                List<Integer> list = new ArrayList<>();
                boolean illegal = false;// 非法位判断
                // 遍历数组
                for(int j = 0;j < num.length;j++){
                    //当前位是 1
                    if((i >> j & 1) == 1){
                        //当前是重复数字,并且前一位是 0,跳过这种情况
                        if(j > 0 && num[j] == num[j-1] && (i >> (j - 1) & 1) == 0){
                            illegal = true;
                            break;
                        }else{
                            list.add(num[j]);
                        }
                    }
                }
                if(!illegal){
                    lists.add(list); 
                }
    
            }
            return lists;
        }
    }
    
    // 作者:windliang
    // 链接:https://leetcode-cn.com/problems/subsets-ii/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-19/
    

    转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1056615746@qq.com

    💰

    Title:78、90、子集

    Count:1.4k

    Author:攀登

    Created At:2020-07-26, 00:19:44

    Updated At:2024-06-15, 15:52:32

    Url:http://jiafeimao-gjf.github.io/2020/07/26/78%E3%80%8190%E3%80%81%E5%AD%90%E9%9B%86/

    Copyright: 'Attribution-non-commercial-shared in the same way 4.0' Reprint please keep the original link and author.

    ×

    Help us with donation