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

491.递增子序列

解题思路

这题的一个坑就是不能进行排序,因为会出现原序列没有的子序列,就会造成错误。然后其他的和子集2是一样的,需要做去重操作,这里不能再使用uesd数组了,应该在每一层的时候定义一个set来判断即可,不会影响到树枝

图上也可以看出,我们是每层的每个节点来收集结果的,但是第一层不需要收集,因为元素要求大于等于2个

class Solution {
private:
vector<int> path;
vector<vector<int>> result;
//叶子节点收集结果,去重逻辑,首次出现的必定包含后续出现的数的搜索范围
void backtracking(vector<int>& nums, int startIndex)
{
     if(path.size()>=2)
     {
        result.push_back(path);
     }
     unordered_set<int> used;    //在每一层上用集合来判断,定义在这就不会影响树枝上的重复选取
     for(int i= startIndex; i<nums.size();i++)
     {
         if((!path.empty() && nums[i] < path.back()) || used.find(nums[i])!=used.end()) //路径空或者要取的数比最后一个小,或者在同一层上已经使用过了,就直接跳过他
           continue;
           used.insert(nums[i]);   //表示这一层已经取过了
           path.push_back(nums[i]);
           backtracking(nums,i+1);
           path.pop_back();
     }
}

public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
              backtracking(nums,0);
              return result;
    }
};
  • 时间复杂度: O(n * 2^n)
  • 空间复杂度: O(n)

46.全排列

题目链接/文字讲解: 代码随想录

解题思路

这题也会使用used来判断选取的元素,这是排列和组合不同的地方,并且这题并不需要startIndex来避免重复选取,[1,2]和[2,1]这种情况也是排列需要的,所以for循环中i=0开始,然后判断used[i]即可,然后从树形结构中可以看出是叶子节点来收集结果的

 

class Solution {
private:
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]==0)     //通过used数组来控制选取哪个元素
        {
            used[i]=1;
            path.push_back(nums[i]);   
            backtracking(nums,used);
            path.pop_back();   //需要控制回溯
            used[i]=0;
        }
        else continue;
    }
}

public:
    vector<vector<int>> permute(vector<int>& nums) {
         vector<int> used(nums.size(),0);
         backtracking(nums,used);
         return result;
    }
};
  • 时间复杂度: O(n!)
  • 空间复杂度: O(n)

47.全排列 II

解题思路

这题的关键在于如何去去重,因为例如[1,1,2]第一个1会出现112的情况,第二个1也会出现112的情况,因此只需要做一个去重的操作即可,去重的逻辑其实和组合是一样的,在树层上去重即可,这样的去重需要对原数组进行排序,我这里是利用false在树层上进行,但是这题也可以使用true在树枝上进行去重

 

class Solution {
private:
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;   //相当于startIndex的作用,跳过已经选取过的元素
            used[i]=1;
            path.push_back(nums[i]);   
            backtracking(nums,used);
            path.pop_back();   //需要控制回溯
            used[i]=0;;
    }
}

public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
         vector<int> used(nums.size(),0);
         sort(nums.begin(),nums.end());
         backtracking(nums,used); 
         return result;
    }
};

这题只是需要理解什么情况会出现排序重复,剩下的去重操作和组合是一样的,搜集结果和全排列是一样的,两题组合起来

拓展

将used[i-1]==1变为条件,也可以去重,但是做的是树枝上的去重,具体结构图如下

 剪枝没有树层上的彻底,多了很多无用搜索

收获

继续加油,今天的题目并不难,主要是两种去重逻辑,一个是不能排序时用局部变量set在同一层去重,一个是可以排序用全局变量used来去重 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值