LeetCode热题(JS版) - 78. 子集

76 阅读2分钟

题目

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

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

示例 1:

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

示例 2:

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

思路

这道题是经典的回溯算法问题。我们可以通过深度优先遍历来枚举出所有可能的子集。具体实现有以下几个步骤:

  1. 初始化结果数组 result 和当前子集 path
  2. 枚举起始位置 start,从 0nums 的长度。
  3. 将当前数 nums[start] 加入到子集中,并将子集 path 添加到结果数组 result 中。
  4. 回溯过程中,依次从子集 path 中删除最后一个元素,得到新的子集 path
  5. 递归调用该过程,更新子集 path 和结果数组 result

根据上面的思路,我们可以写出下面的 TypeScript 代码:

function subsets(nums: number[]): number[][] {
  const result: number[][] = [];
  const path: number[] = [];

  function backtrack(start: number) {
    // 将当前子集添加到结果数组中
    result.push([...path]);

    for (let i = start; i < nums.length; i++) {
      // 加入当前数
      path.push(nums[i]);
      // 继续递归构造子集
      backtrack(i + 1);
      // 回溯,撤销选择
      path.pop();
    }
  }

  backtrack(0);
  return result;
}

上述代码中,我们使用了回溯算法来枚举所有可能的子集。其中,backtrack 函数为主要递归函数,用于构造子集。在该函数中,我们首先将当前子集 path 添加到结果数组 result 中,然后依次选取后面的元素并递归调用 backtrack 函数,最后回溯撤销选择。

image.png

复杂度分析

  • 时间复杂度为 O(n×2n)O(n \times 2^n)

对于本题的时间复杂度分析,我们需要考虑回溯函数的执行次数。由于每个元素都有两种状态(选或不选),因此回溯函数中的循环执行次数为 2n2^n,其中 nn 表示原数组的长度。又因为在构造子集时需要复制数组和遍历数组,因此总的时间复杂度为 O(n×2n)O(n \times 2^n)

  • 空间复杂度为 O(n)O(n)

对于本题的空间复杂度,我们需要考虑递归调用时使用的栈空间和中间结果数组的空间。由于回溯算法是一种递归算法,因此在执行函数时会产生额外的栈空间,其空间复杂度为 O(n)O(n)。同时,在递归调用过程中,还需要使用一个中间结果数组来存储子集,其空间复杂度也为 O(n)O(n)。因此,总的空间复杂度为 O(n)O(n)