前端算法(56)

103 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

题目

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

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

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

题目解析

思路一

我们这里需要考虑如何处理重复的元素,在一个子集里可以出现相同的元素,但是不同的集合之间重复的元素不可再出现第二次,所以我们需要在进入循环时就要对重复元素进行处理,不过在处理前要先将nums进行排序,在循环开始时,判断i是否大于startIndex以及当前元素是否和前一个元素相同,若相同,则跳过此次循环,因为只有在i大于startIndex时,才表明前一个元素已被使用过,而使用过的元素不可以在同一个循环中再次被使用,因此设定这个判断条件,是否进入递归

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var subsetsWithDup = function(nums) {
    const res = [], path = [];
    const sortNums = nums.sort((a, b) => a - b);
    const backtracking = function(startIndex) {
        res.push([...path]);
        if(startIndex > nums.length - 1) {
            return;
        }
        for(let i = startIndex; i < nums.length; i++) {
            if(i > startIndex && sortNums[i] === sortNums[i - 1]) {
                continue;
            }
            path.push(sortNums[i]);
            backtracking(i + 1);
            path.pop();
        }
    }
    backtracking(0);
    return res;
};

思路二

我们先进行排序, 这样可以排序后相等的元素相邻方便去重,在声明一个接收结果数组,声明一个递归函数,作用于把当前路径加入结果集中,如果当前元素跟上一个元素相等,上一个元素如果没有选择的话,当前元素也不能选择,否则就会有重复,进行判断是否选择这个数,在将状态位置为已选择,在基于选这个数,继续递归,传入的是i+1,不是index+1,最后撤销选这个数,在还原状态

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var subsetsWithDup = function (nums) {
  nums.sort((a, b) => a - b);
  let visited = new Array(nums.length).fill(false);
  let res = [];
  const backtrack = (index, path) => {
    res.push(path.slice());
    for (let i = index; i < nums.length; i++) {
      if (
        (nums[i] == nums[i - 1] && visited[i - 1]) ||
        nums[i] != nums[i - 1]
      ) {
        path.push(nums[i]);
        visited[i] = true;
        backtrack(i + 1, path);
        path.pop();
        visited[i] = false;
      }
    }
  }
  return res
}