leetcode 77.Combinations【递归】【medium】|刷题打卡

324 阅读3分钟

一、题目描述:

leetcode 77: 给定n和k,返回[1,2...n]中子集的集合,这些子集的元素个数为k,不限制顺序。

二、思路分析:

1.类别

递归、回溯

2.做题思路

因此可以写出递归函数是void recurAns(int l,int r,int k,vector<int> &path,vector<vector<int>> &ans),l和r表示区间的左和右界,path是递归求出来的单个满足条件的子集,ans是最后返回的集合。返回的边界条件有两个:当前的子集path的数量等于k以及左界大于右界。对于一个元素,考虑两种情况:

  1. 元素不在当前子集里:
  • 不添加元素(无操作)
  • 剩余部分:recurAns(l+1,r,k,path,ans);
  1. 元素在当前子集里:
  • 添加元素进入path:path.push_back(l);
  • 剩余部分: recurAns(l+1,r,k,path,ans);
  • 恢复现场(回溯):path.pop_back(); 由于最后的结果由边界条件产生,也就是当path的元素个数为k个时,ans.push_back(path);这一步,因此可以直接把这两个操作并列写下来,因为最后的结果都将被加入ans。 具体代码如下:
void recurAns(int l,int r,int k,vector<int> &path,vector<vector<int>> &ans)     {
        if(path.size()==k){
            ans.push_back(path);
        }
        if(l>r)
            return;
        recurAns(l+1,r,k,path,ans);
        
        path.push_back(l);
        recurAns(l+1,r,k,path,ans);
        path.pop_back();
    }

但这样会产生重复项,因为回溯的时候会在这一边界条件后再次调用自己。因此应该加入ans后直接跳出函数。

	void recurAns(int l,int r,int k,vector<int> &path,vector<vector<int>> &ans)     {
        if(path.size()==k){
            ans.push_back(path);
            return; //避免回溯带来的重复
        }
        if(l>r)
            return;
        recurAns(l+1,r,k,path,ans);
        
        path.push_back(l);
        recurAns(l+1,r,k,path,ans);
        path.pop_back();
    }

主函数只需要传入整个1到n数组的左界和右界,给定的k和空集path与空集ans即可。

    vector<vector<int>> combine(int n, int k) 
    {
        vector<vector<int>> ans;
        vector<int> path;
        recurAns(1,n,k,path,ans);
        return ans;
    }
};

三、AC 代码:

class Solution {
public:
    void recurAns(int l,int r,int k,vector<int> &path,vector<vector<int>> &ans)     {
        if(path.size()==k){
            ans.push_back(path);
            return;
        }
        if(l>r)
            return;
        recurAns(l+1,r,k,path,ans);
        
        path.push_back(l);
        recurAns(l+1,r,k,path,ans);
        path.pop_back();
    }
    
    vector<vector<int>> combine(int n, int k) 
    {
        vector<vector<int>> ans;
        vector<int> path;
        recurAns(1,n,k,path,ans);
        return ans;
    }
};

四、总结:

1.这题虽然写的很快但是递归不够优雅,会出现重复的项,主要是因为回溯的时候重复在ans中加入了相同的path。网上应该还有更好的回溯版本,这里只是一个参考的解法。关于这种解法的问题会在leetcode 78.Subsets【递归】【medium】中有更详细的解释。

  1. 关于vector和set的转换 对于重复项,另一个思路是可以使用C++ STL中的set进行去重,由于主函数返回的要求是vector,我们可以在主函数中先把vector转化成set,这样就能够自动去重,然后再把set中的内容赋值到ans中,具体代码如下:
set<vector<int>> st(ans.begin(), ans.end());
ans.assign(st.begin(), st.end());

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情