回溯算法-子集问题-递增子序列

leetcode491. 递增子序列
这题非常不一样
首先我们看常规思维:
bool型used[]配合sort去重,但是如果sort了,不论是从大到小还是从小到大
从大到小:第一个添加的元素最大,后面最多再添加相同的元素,比如[[7],[7,7],[7,7,7]]。
从小到大:第一个添加的元素必定最小,那这就相当于把该集合的所有不重复的子集全部取一遍,这和子集2那道题目没区别,比如[[1],[1,2],[1,2,3],[1,2,3,4]…]
所以肯定不能用sort配合bool的used[]
那么能不能只用used呢,题目给的例子好像已经把相同元素排在一起了,当然不行,那只是巧合罢了,后面有一个案例就是题目给的数组没有吧相同元素排在相邻位置,比如[1,2,3,4,5,6,7,8,9,10,1,1,1,1,1],可以看到,开头的1和后面的1111是分开的,因此我们只能使用子集2里面的另一种去重方式----set去重。
set去重的代码如下:

class Solution {
private:
    vector<int> path;
    vector<vector<int>> result;
    void backtracking(int startIndex,vector<int> nums){//1.确定函数的参数和返回值
        //子集问题在树的所有节点均收集结果
        if(path.size()>=2){
            result.push_back(path);
        }
        //2.确定递归的终止条件
       if(startIndex==nums.size()) return ;
        //3.确定单层递归的逻辑
        unordered_set<int> set;//使用set对本层元素去重
        for(int i=startIndex;i<nums.size();i++){
            //两种不能加入path的情况
            if((!path.empty()&&nums[i]<path.back())||set.find(nums[i])!=set.end()){
                continue;
            }
            path.push_back(nums[i]);//处理
            set.insert(nums[i]);
            backtracking(i+1,nums);//递归
            path.pop_back();//回溯,这里不需要回溯set,每一层都会新开一个set对那一层进行去重
        }
    }
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        vector<bool> used(nums.size(),false);
        backtracking(0,nums);
        return result;
    }
};

本题给的nums数组范围在(-100,100),因此还可以使用数组来代替set去重,因为在set去重时,相对于每一树层都要新开一个set以及set每次都要hash印射,数组明显空间复杂度和时间复杂度更低
注意:

  1. 不论是set还是数组,都要在for循环外面定义,并且也不需要回溯
    因为我们本质就是用数组或者set对每一树层去重,在回溯算法树中,每一次调用backtracking()就是进入下一个树层,每次调用back函数的时候都会对每一个树层新开一个数组或者set用来对当前层进行去重,for循环的意思是每一树层左右横向遍历当前树层元素
  2. 回溯问题虽然有固定的模版,但是具体问题具体分析,这还是第一次见到去重的set不需要回溯的,打开眼界了。
//3.确定单层递归的逻辑
        int used[201]={0};//使用数组对本层元素去重
        for(int i=startIndex;i<nums.size();i++){
            //两种不能加入path的情况(nums[i]+100的原因是数组下标只能从0开始)
            if((!path.empty()&&nums[i]<path.back())||used[nums[i]+100]==1){
                continue;
            }
            path.push_back(nums[i]);//处理
            used[nums[i]+100]=1;
            backtracking(i+1,nums);//递归
            path.pop_back();//回溯,这里不需要回溯set,每一层都会新开一个set对那一层进行去重
        }

最后看一下之前的错误写法:

class Solution {
private:
    vector<int> path;
    vector<vector<int>> result;
    void backtracking(int startIndex,vector<int> nums,vector<bool> used){//1.确定函数的参数和返回值
        //子集问题在树的所有节点均收集结果
        if(path.size()>=2){
            result.push_back(path);
        }
        //2.确定递归的终止条件
       if(startIndex==nums.size()) return ;
        //3.确定单层递归的逻辑
        for(int i=startIndex;i<nums.size();i++){
            //两种不能加入path的情况
            if((!path.empty()&&nums[i]<path.back())||(i>0&&nums[i]==nums[i-1]&&used[i-1]==false)){
                continue;
            }
            path.push_back(nums[i]);//处理
            used[i]=true;
            backtracking(i+1,nums,used);//递归
            used[i]=false;//回溯
            path.pop_back();
        }
    }
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        vector<bool> used(nums.size(),false);
        sort(nums.begin(),nums.end());
        backtracking(0,nums,used);
        return result;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值