leetcode 78.Subsets【递归】【medium】|刷题打卡

388 阅读2分钟

一、题目描述:

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 春招闯关活动」, 点击查看 活动详情