LeetCode 78.子集

527 阅读1分钟

「这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战」。

题目:给定一个整数数组,数组中的元素不存在相同的两个。要求返回该数组所有可能的子集。子集中不可包含重复的子集,可按照任意顺序返回。

解题思路

回想LeetCode 46全排列那题,那题是得到所有的全排列,解决思路是构建一个递归树,一次次递归,最终将所有的叶子结点的值作为结果,要求所有返回的值不重复,因此那题是采用visit数组来将每次访问到的元素进行标记。再看LeetCode 39求所有的组合数,也是要求结果不能重复,此处使用的则是每次递归设置一个起始点,递归中从这个起始点开始求解。其要得到的是符合条件的叶子和中间结果。

本题和上面两题有点类似,其最终目的是寻找树的所有结点。我们可以想象,在每次构建树的时候,我们都将结点加入一个新的容器,最终这个容器的总和不就是我们的答案。并且因为我们要求元素不重复,那可以为数组设置一个起始点,起始点保证了最终结果的唯一性,代码如下:

ArrayList<List<Integer>> res = new ArrayList<>(); // 总容器
    ArrayList<Integer> temp = new ArrayList<>(); //中间容器
    public List<List<Integer>> subsets(int[] nums) {
        dfs(nums, 0);
        return res;
    }

    public void dfs(int[] nums, int start){
        res.add(new ArrayList<>(temp));
        for(int i=start;i<nums.length;i++){
            temp.add(nums[i]);
            dfs(nums, i+1);
            temp.remove(temp.size()-1);
        }
    }

上述代码就是经典的回溯,回溯终止条件即为判断当前起始点是否大于数组的长度,如果大于则不会继续回溯下去。

思路二

从前往后遍历数组,每当遇到数组中的一个数,就将该数和已有的所有子集进行组合,形成新的子集,这样当数组遍历完毕,子集也计算完毕,代码如下:

public List<List<Integer>> subsets2(int[] nums) {
        ArrayList<List<Integer>> res2 = new ArrayList<>();
        res2.add(new ArrayList<>());
        for(int i=0;i<nums.length;i++){
            int size = res2.size();
            for(int j=0;j<size;j++){
                ArrayList<Integer> temp2 = new ArrayList<>(res2.get(j));
                temp2.add(nums[i]);
                res2.add(temp2);
            }
        }
        return res2;
    }

上述方法的时间复杂度为O(N)O(N), 空间复杂度为O(1)O(1)