返回

穷举子集之美:LeetCode 90. 子集 II(重复元素)的递归解法

前端

LeetCode 题解:90. 子集 II:递归+for循环+回溯,JavaScript,详细注释

我们先来认识一下什么是子集。子集是指一个集合的元素中的一些元素构成的集合,包括空集。给定一个包含重复元素的集合,我们的目标是找到它所有的子集。

这道题最直观的解法就是使用递归。递归的基本思路是:对于一个集合,我们可以把它分解成一个元素和剩余的元素,然后对剩余的元素进行递归,将元素加入或不加入到子集中,以此穷举出所有的子集。

为了避免重复,我们在递归过程中使用一个 visited 数组来记录每个元素是否已经加入到子集中。具体步骤如下:

  1. 创建一个函数 subsetsWithDup,接收一个数组 nums 作为参数。
  2. 创建一个结果数组 result 和一个 visited 数组,其中 visited 数组与 nums 数组长度相等,并初始化为 false
  3. 调用递归函数 dfs,从索引 0 开始遍历 nums 数组。
  4. dfs 函数中,如果 visited[i]true,则说明元素 nums[i] 已经加入到子集中,直接返回。否则,将 visited[i] 设为 true,将 nums[i] 加入到子集 subset 中,并调用 dfs 函数从索引 i+1 开始继续遍历 nums 数组。
  5. dfs 函数中,如果到达了数组的末尾,则将子集 subset 加入到结果数组 result 中。
  6. dfs 函数中,将 visited[i] 设回 false,并将 nums[i] 从子集 subset 中弹出。
  7. 返回 result
/**
 * LeetCode 90. 子集 II
 *
 * @param {number[]} nums
 * @return {number[][]}
 */
const subsetsWithDup = function (nums) {
  if (nums === null || nums.length === 0) {
    return [[]];
  }

  nums.sort((a, b) => a - b); // 排序数组,以便去除重复元素

  const result = [];
  const visited = new Array(nums.length).fill(false);
  const dfs = (i, subset) => {
    if (i === nums.length) {
      result.push(subset.slice());
      return;
    }

    if (visited[i]) {
      return;
    }

    // 选择当前元素
    subset.push(nums[i]);
    visited[i] = true;
    dfs(i + 1, subset);

    // 不选择当前元素
    subset.pop();
    visited[i] = false;
    dfs(i + 1, subset);
  };

  dfs(0, []);

  return result;
};

时间复杂度

解法的时复杂度为O(2^n),其中n为输入数组的长度。

空间复杂度

解法的空间复杂度为O(n),其中n为输入数组的长度。