代码随想录算法训练营第二十九天| LeetCode 491.递增子序列、46.全排列、47.全排列 II

一、LeetCode 491.递增子序列

题目链接/文章讲解/视频讲解:https://programmercarl.com/0491.%E9%80%92%E5%A2%9E%E5%AD%90%E5%BA%8F%E5%88%97.html

状态:已解决

1.思路 

        这道题看似和90题差不多,都是求子集并且有重复元素,但实则大有不同。因为这道题要求给出非递减子序列,因此我们不能用排序的方式来去重。那该怎么办呢?其实40题的去重逻辑的核心就是要我们在同层递归(树层)的时候,不再选取已经出现过的值

        在40题中,我们用nums[i]==nums[i-1]和used[i-1]==0两个共同的条件来判定此时是树层重复还是树枝重复。那失去了nums[i]有序的这个条件又如何判定了?其实很简单,因为我们只需要控制同一树层的时候,不选取已经选取过的值,也就是说,只要我们在树层开始前设个数组,让这个数组存放已经出现过的值,那么如果这次for循环选取的值数组中已经有了,就说明该值已经出现过,我们跳过不做后续操作就好。(做完了发现可行并且老师也是这个做法!!)

2.代码实现

       每层递归用来存放for循环已经选取过的值的数组可以采用set实现,也可以用map实现。我这里用的map,老师讲解用的set。        

class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;
    void backtracking(vector<int>& nums,int startIndex){
        if(path.size()>1)//注意集合中至少两个元素
            result.push_back(path);
        if(startIndex>=nums.size()){
            return ;
        }
        unordered_map<int,int> used;
        for(int i=startIndex;i<nums.size();i++){
            if(i>startIndex && used.find(nums[i])!= used.end() && used[nums[i]]==1){
                continue;
            }//判断是否已经出现过该值,used.find(nums[i])!= used.end()很重要
            if(path.size()==0 || nums[i]>=path[path.size()-1]){
                path.push_back(nums[i]);
                used[nums[i]]=1;
                backtracking(nums,i+1);
                path.pop_back();
            }
        }
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        path.clear();
        result.clear();
        backtracking(nums,0);
        return result;
    }
};

二、46.全排列

题目链接/文章讲解/视频讲解:https://programmercarl.com/0046.%E5%85%A8%E6%8E%92%E5%88%97.html

状态:已解决

1.思路 

        排列与组合的区别在于排列要看集合元素之间的顺序,而组合不看,也就是说,对排列来说,{1,2}和{2,1}是两个集合,而对组合而言,二者是同一个集合。我们在做组合题时曾经说过,为了避免组合时出现相同组合{1,2}、{2,1},令startIndex根据上层i的取值来取,使得startIndex>i,因此,要使组合变排列,只需将startIndex的取值范围变一下,变为从0开始取值,确保原数组后面的元素也可以作为集合中靠前的元素。不过,为了确保前后取到同一个元素值,我们需要记录下来每层递归所取的值,让后续递归不再取它们,也就是要进行树枝去重,用used数组实现。

2.完整代码

class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;
    void backtracking(vector<int>& nums,vector<int>& used){
        if(path.size() == nums.size()){
            result.push_back(path);
            return ;
        }
        for(int i=0;i<nums.size();i++){
            if(used[i]==1) continue;
            path.push_back(nums[i]);
            used[i] = 1;
            backtracking(nums,used);
            used[i] = 0;
            path.pop_back();
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        vector<int> used(nums.size(),0);
        backtracking(nums,used);
        return result;
    }
};

三、47.全排列 II

题目链接/文章讲解/视频讲解:https://programmercarl.com/0047.%E5%85%A8%E6%8E%92%E5%88%97II.html

状态:已解决

1.思路 

        因为有重复元素,又是排列题,故此题实则就是40题双重去重和46题的一个结合。需要在46题一个树枝去重和一个树层去重的操作,具体怎么操作可以去看40题的解法。注意,对于used操作的用法,在组合和排序中,used数组含义是相同的,但是组合主要是用来佐证确定树层去重,而排序是用来确保树枝去重的。

2.代码实现

class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;
    void backtracking(vector<int>& nums,vector<int>& used){
        if(path.size() == nums.size()){
            result.push_back(path);
            return ;
        }
        for(int i=0;i<nums.size();i++){
            if(i>0 && nums[i]==nums[i-1] && used[i-1]==0) continue;
            if(used[i]==1) continue;
            path.push_back(nums[i]);
            used[i]=1;
            backtracking(nums,used);
            used[i]=0;
            path.pop_back();
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<int> used(nums.size(),0);
        backtracking(nums,used);
        return result;
    }
};

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值