题目描述
原题链接:子集 II
给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
示例
输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
1 <= nums.length <= 10 -10 <= nums[i] <= 10
解答
本题是LeetCode-78. 子集的变体,仅仅是由条件中 无重复元素 改为了 可能包含重复元素。
这类题用回溯法来解会更简单些,这里贴一个笔者常用的回溯模板:
function backtracking(list) {
const result = [] // 定义结果搜集数组
const temp = [] // 定义辅助数组或变量
// 定义辅助函数
function helper(idx) {
if (符合条件) {
result.push([...temp]) // 如果是一个可行解就添加的结果搜集数组
}
// 根据索引遍历待搜索的区域
for (let item of list.slice(idx)) {
temp.push(item) // 辅助数组结合新成员改变状态
if (符合条件) {
helper(list.indexOf(item) + 1) // 如果符合特定条件则继续深入搜索
}
temp.pop() // 回归原来的状态
}
}
helper(0) // 调用辅助函数
return result // 返回结果
}
在题LeetCode-78. 子集中,因为不存在重复数,所以一定可以继续深入搜索,这里贴下 78 题的解法:
var subsets = function (nums) {
const res = []
const tempRes = []
// 从索引 idx 开始获取
const helper = idx => {
res.push([...tempRes])
for (let i = idx; i < nums.length; i++) {
tempRes.push(nums[i])
helper(i + 1)
tempRes.pop()
}
}
helper(0)
return res
}
但在本题中,因为可能包含重复数,所以在继续深入搜索时需判断这个数是否重复了,这里我们采用先排序,并定义一个 Set 类型的变量,以辅助数组转换为字符串后作为一个 value 存到 set 中,仅当 set 不包含同一个 value 才继续递归搜索:
var subsetsWithDup = function (nums) {
let res = []
let tempRes = []
let set = new Set()
nums.sort((a, b) => a - b)
const helper = idx => {
res.push([...tempRes])
for (let i = idx; i < nums.length; i++) {
tempRes.push(nums[i])
const value = tempRes.join('-')
if (!set.has(value)) {
set.add(value)
helper(i + 1)
}
tempRes.pop()
}
}
helper(0)
return res
}
复杂度分析:
- 时间复杂度:排序数组复杂度 O(NlogN),递归搜索中的最坏情况下是 nums 中无重复元素,需要枚举其所有 2^n 个子集,每个子集加入答案时需要拷贝一份,耗时 O(n),即复杂度为 O(n * 2^n)。