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来去重