一、题目描述:
leetcode 78:给定一个元素无重复的集合nums,求这个集合的所有子集。
二、思路分析:
1.类别
递归、回溯
2.解题思路
这一题的思路和leetcode 77.Combinations【递归】【medium】中非常类似。以样例中的[1,2,3]为例,从左到右每一个元素都会有在子集和不在子集两种情况:
三、AC 代码:
我的解法是源于leetcode 77.Combinations【递归】【medium】,区别只是没有k的限制,但是这种解法需要去重。
class Solution {
public:
void recurAns(int l,int r,vector<int>& nums,vector<int> &path,vector<vector<int>> &ans)
{
if(l>r)
return;
recurAns(l+1,r,nums,path,ans);
ans.push_back(path);
path.push_back(nums[l]);
recurAns(l+1,r,nums,path,ans);
ans.push_back(path);
path.pop_back();
}
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ans;
vector<int> path;
recurAns(0,nums.size()-1,nums,path,ans);
set<vector<int>> st(ans.begin(), ans.end());
ans.assign(st.begin(), st.end());
return ans;
}
};
但是为什么会出现重复的子集呢?
以一个最简单的[1,2]为例子,把整个程序运行的过程绘制如下:(从左到右、从上到下深度优先执行)
会发现这种写法会在path不变的情况下,ans会在path数组pop之后(恢复现场),再次添加一次,如图中
ans push []会出现两次。这就会导致如果不去重,那么[1,2]的执行结果会是这样:[[],[2],[],[1],[1,2],[1]]。
使用更简洁的回溯法,应该是下面这样的。只保留恢复现场的那一次,不再需要考虑加入空集的那一行。path.push_back(nums[l]);因为是按照从左到右的顺序,可以直接使用for循环。
class Solution {
public:
void recurAns(int l,int r,vector<int>& nums,vector<int> &path,vector<vector<int>> &ans)
{
ans.push_back(path);
for(int i=l;i<=r;i++){
path.push_back(nums[i]);
recurAns(i+1,r,nums,path,ans);
path.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ans;
vector<int> path;
recurAns(0,nums.size()-1,nums,path,ans);
return ans;
}
};
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情