力扣 90. 子集 II [先导题:47. 全排列 II和78. 子集]
给你一个整数数组 nums
,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
示例 1:
输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
提示:
1 <= nums.length <= 10
-10 <= nums[i] <= 10
题解#
先导题目是:力扣 78. 子集 和 力扣 47. 全排列 II
力扣 78. 子集是此题的基础,基本结构没有变化,思路复制过来:
在递归函数中,每次传入当前下标idx
,路径使用vector
记录选择的元素,idx
其对应的元素,有两种情况:选和不选,分别操作后继续进入递归函数:
- 选:在当前路径
cur
中加入元素,再进入递归 - 不选:
cur
退选元素,再进入递归
当idx
到达边界时,说明当前路径已经遍历完所有元素,记录当前路径cur
,结束
而重复元素的解决办法来自力扣 47. 全排列 II,复制过来稍作修改:
因为可以重复,所以先来一个排序,方便后面处理重复,当面对下标idx
时,如果nums[idx]与nums[idx-1]
相等,应该加以判断是否选择idx
画图捋一下[1,1,2]
,取第二个1
为1'
,[1,1',2]
,
面对1'
时,因为1
等于1'
:
- 如果之前选择过
1
,说明之前肯定以[1,1']
为开头去搜索其余的元素了,此时如果选择1'
则会以[1',1]
为开头去重复计算 - 如果之前没有选择过
1
,现在选择1'
,不会造成重复.
所以前面说的加以判断是判定之前的1是否被使用过,如果没有用过才可以选择当前的1'
,如果用过就没有选择当前1'
,对应的判断语句是idx>0&&nums[idx]==nums[idx-1]&&!flag[idx-1])
。
注意这里有47题不一样的在于:如果判断得出选当前1'
会造成重复,则不能直接return
(在47题中是continue
),而是直接进入递归函数,即当前路径cur不选择当前1'
。
查看代码
class Solution {
public:
vector<vector<int>> res;
bool flag[10]={false};
void dfs(vector<int>& cur,vector<int>& nums,int idx,int len){
if(idx==len)//cur已经遍历完所有元素
{
res.emplace_back(cur);
return;
}
//特殊情况:判断和去重
//当前用过,就不选择当前元素,直接进入递归函数
//如果和上一个相等且上一个没有选过,不选择当前元素,直接进入递归函数
if(flag[idx]||(idx>0&&nums[idx]==nums[idx-1]&&!flag[idx-1])){
dfs(cur,nums,idx+1,len);
return;
}
//情况一:选择idx
//标志位修改
flag[idx]=!flag[idx];
//选择idx
cur.emplace_back(nums[idx]);
dfs(cur,nums,idx+1,len);
//标志位复原
flag[idx]=!flag[idx];
//退选idx
cur.pop_back();
//情况二:不选择idx
dfs(cur,nums,idx+1,len);
}
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
vector<int> cur;
sort(nums.begin(),nums.end());
// work(cur,nums,0);
dfs(cur,nums,0,nums.size());
return res;
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2019-03-28 计蒜客 方程的解数 dfs
2019-03-28 计蒜客 买书 dfs