热题100 - 78. 子集

44 阅读2分钟

题目描述:

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

  示例 1:

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

示例 2:

输入: nums = [0]
输出: [[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同

思路:

dfs深度优先搜索的经典题目,思想就是穷举所有可能的路径。

因为之前总结说dfs喜欢for循环,但是这里用for循环是不对的,正确的做法是一个dfs里调用两个dfs,分为”有“和”无“两个分支,刚好有n层,一共是2^n次方个解。

那其实之前的很多题,说dfs喜欢循环,是因为有循环那么多个分支而已。很多判断是为了剪枝。

实现:

dfs都要有的res,holder,输入数组nums,再就是起点start。

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> holder = new ArrayList<>();
        dfs(res, holder, 0, nums);
        return res;
    }

    private void dfs(List<List<Integer>> res, List<Integer> holder, int start, int[] nums) {
        if (start == nums.length) {
            res.add(new ArrayList<Integer>(holder));
            return;
        }

        holder.add(nums[start]);
        dfs(res, holder, start+1, nums);
        holder.remove(holder.size()-1);
        dfs(res, holder, start+1, nums);
    }
}

另外去年9月还写出来一个基于位运算的实现,主要利用了子集数量是2的n次方的性质。

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        //数组长度大于30的时候这个方法就不适用了
        // 主要依赖于这个全部子集的属性,n个元素,一共有2^n个子集
        int n  = nums.length;
        int total = 1 << n;
        List<List<Integer>> res = new ArrayList<>();
        for (int i = 0; i < total; i++) {
            // 每个i对应一个结果,需要一个List
             List<Integer> list = new ArrayList<>();
            for (int j = 0; j < n; j++) {
                //对数组元素遍历
                if (((i >> j) & 1) == 1) {
                    // i >> j是想把每一位都挪到最低位,然后和1取与,1就保留,0就算了
                    list.add(nums[j]);
                }
            }
            res.add(new ArrayList<Integer>(list));
        }
        return res;
    }
}

仅供参考。