二十天刷题计划-- 子集

112 阅读2分钟

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

1.题目

给你一个整数数组 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 中的所有元素 互不相同


思路

深度优先搜索

当问题需要 "回头",以此来查找出所有的解的时候,使用回溯算法。即满足结束条件或者发现不是正确路径的时候(走不通),要撤销选择,回退到上一个状态,继续尝试,直到找出所有解为止

如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!

其实子集也是一种组合问题,因为它的集合是无序的,子集{1,2} 和 子集{2,1}是一样的。

那么既然是无序,取过的元素不会重复取,写回溯算法的时候,for就要从startIndex开始,而不是从0开始!

求排列问题的时候,就要从0开始,因为集合是有序的,{1, 2} 和{2, 1}是两个集合,排列问题我们后续的文章就会讲到的。

以示例中nums = [1,2,3]为例把求子集抽象为树型结构,

遍历这个树的时候,把所有节点都记录下来,就是要求的子集集合。

复杂度分析

时间复杂度:O(n \times 2 ^ n)O(n×2 n )。一共 2^n2 n 个状态,每种状态需要 O(n)O(n) 的时间来构造子集。

空间复杂度:O(n)O(n)。临时数组 tt 的空间代价是 O(n)O(n),递归时栈空间的代价为 O(n)O(n)。


代码

/**
 * @param {number[]} nums
 * @return {number[][]}
 */

var subsets = function(nums) {
    let res = [];
    let route = [];
    backtrack(nums, 0, route);
    return res;

    function backtrack(nums, start, route){
        res.push(route.slice());
        for(let i = start; i < nums.length; i ++){
            route.push(nums[i]);
            console.log(route)
            backtrack(nums, i + 1, route);
            route.pop();
        }
    }
};