zoukankan      html  css  js  c++  java
  • 力扣 78.子集

     78. 子集

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

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

    示例:

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

    法一:利用二进制的位运算

    思路:

    利用位数为数组长度的二进制数,这个二进制数所能表示的元素个数刚好等于这个幂集的子集的个数,且二进制数的每个数代表了一种子集的选择情况,数位为1则表示选该数,数位0则表示不选该数

     1 class Solution {
     2     public List<List<Integer>> subsets(int[] nums) {
     3         // 利用位数为数组长度的二进制数,这个二进制数所能表示的元素个数刚好等于这个幂集的子集的个数
     4         // 且二进制数的每个数代表了一种子集的选择情况,数位为1则表示选该数,数位0则表示不选该数
     5         ArrayList<List<Integer>> list = new ArrayList<List<Integer>>();
     6         for(int i = 0; i < (1 << nums.length); i++){
     7             List<Integer> sub = new ArrayList<Integer>();
     8             for(int j = 0; j < nums.length; j++){
     9                 if(((i >> j) & 1) == 1)
    10                     sub.add(nums[j]);
    11             }
    12             list.add(sub);
    13         }
    14         return list;
    15     }
    16 }

    时间复杂度分析:

    外层循环执行2^n次,内层循环每轮执行n次,所以时间复杂度为 O(N*2^N)

    法二:枚举法

    思路:

    枚举每个元素,把这个元素添加到现有的所有集合,然后把这个新集合加入list中

     1 class Solution {
     2     public List<List<Integer>> subsets(int[] nums) {
     3         // 枚举每个元素,把这个元素添加到现有的所有集合,然后把这个新集合加入list中
     4         ArrayList<List<Integer>> list = new ArrayList<List<Integer>>();
     5         list.add(new ArrayList());  // 添加一个空集合
     6         for(int i = 0; i < nums.length; i++){
     7             int size = list.size();
     8             for(int j = 0; j < size; j++){
     9                 List<Integer> sub = new ArrayList<>(list.get(j));
    10                 sub.add(nums[i]);
    11                 list.add(sub);
    12             }
    13         }
    14         return list;
    15     }
    16 }

    算法复杂度分析:

    每次都把当前元素添加到list的所有集合中,所以每轮下来list中集合数量可以加倍,所以总的时间复杂度为O(1 + 2^1 + 2^2 +....+ 2^(n-1)) = O(2^n - 1) = O(2^n),可以看出这个算法的效率比第一种方法要高

    法三:(回溯法)

    思路:

    回溯法,对每个元素进行选与不选的判断

     1 class Solution {
     2     public List<List<Integer>> subsets(int[] nums) {
     3         // 回溯法,对每个元素进行选与不选的判断
     4         ArrayList<List<Integer>> list = new ArrayList<List<Integer>>();
     5         ArrayList<Integer> sub = new ArrayList<Integer>();
     6         traceBack(nums, 0, list, sub);
     7         return list;
     8     }
     9     // i表示元素的下标,sub用来存储一个子集
    10     public void traceBack(int[] nums, int i, ArrayList<List<Integer>> list, ArrayList<Integer> sub){
    11         if(i >= nums.length){
    12             list.add(new ArrayList<Integer>(sub));
    13             return;
    14         }
    15         // 不选当前元素
    16         traceBack(nums, i + 1, list, sub);
    17 
    18         // 选当前元素
    19         sub.add(nums[i]);
    20         traceBack(nums, i + 1, list, sub);
    21         sub.remove(sub.size() - 1);         // 回溯到添加元素前的状态 
    22     }
    23 }

    复杂度分析:

    每个元素都有选与不选两种情况,所以时间复杂度为 O(2^n)

    递归栈的最大深度为 n层,但是每层所操作的都是同一个对象sub, 所用的空间都不是很大,这里空间复杂度不太会分析,只知道如果元素个数太多会导致栈的深度太深而导致栈溢出

    文章参考:

    https://leetcode-cn.com/problems/subsets/solution/er-jin-zhi-wei-zhu-ge-mei-ju-dfssan-chong-si-lu-9c/

  • 相关阅读:
    linux上实现jmeter分布式压力测试(转)
    The more,the better。
    DP_括号匹配序列问题
    MySQL基础语句
    大端模式和小端模式
    C++:bitset用法
    TCP三次握手和四次握手
    静态库与动态库
    DP_最长公共子序列/动规入门
    二维数组和指针
  • 原文地址:https://www.cnblogs.com/hi3254014978/p/12878462.html
Copyright © 2011-2022 走看看