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印射,数组明显空间复杂度和时间复杂度更低
注意:
- 不论是set还是数组,都要在for循环外面定义,并且也不需要回溯
因为我们本质就是用数组或者set对每一树层去重,在回溯算法树中,每一次调用backtracking()就是进入下一个树层,每次调用back函数的时候都会对每一个树层新开一个数组或者set用来对当前层进行去重,for循环的意思是每一树层左右横向遍历当前树层元素 - 回溯问题虽然有固定的模版,但是具体问题具体分析,这还是第一次见到去重的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;
}
};