本文已参与「新人创作礼」活动,一起开启掘金创作之路。
题目
给你一个整数数组 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
}