一、递增子序列
1.题目
给你一个整数数组 nums
,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
2.思路
这道题不能进行排序,排序就不是子序列了,所以之前的标记数组去重逻辑不能使用。可以采用哈希表进行去重。
有两个需要注意的点:
1.递增
2.不能重复
以{4,7,6,7}举例子,下图是取值过程。
回溯三部曲:
1.传入参数
单个集合进行取值,所以需要startIndex。
2.终止条件
题目要求递增子序列中 至少有两个元素,因此当path收集元素数大于1时即可加入result。
3.单层递归逻辑
去重:同一父节点下的同层上使用过的元素就不能再使用了。使用unordered_set<int>(哈希表可以快速判断是否有元素出现在集合里代码随想录 (programmercarl.com))类型进行标记。
注意:去重标记只是在该层进行标记,进入下次递归(进入下一层)后,会产生一个新的unordered_set<int>数据进行标记。所以,回溯时,不需要清除!区别于之前的数组去重(数组重新标为false是因为让该层不能收集该元素。)
递增:只需要和path最后一个元素比较,小于则跳过该次收集。
总体代码:
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& nums,int startIndex){
if(path.size() > 1){
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;
}
};
二、全排列
1.题目
2.思路
以集合{1,2,3}举例子。
排序题目遍历都要从集合的第一个元素开始遍历,但是需要额外的数组判断该元素是否被用过。
1.传入参数
used是全局变量,用来标记元素是否用过。
2.终止条件
当path收集的元素数量等于nums的数量,说明集合排序完成。
3.单层递归逻辑
当used的元素位为false,path则进行收集,进行递归等操作。
总体代码:
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& nums,vector<bool>& used){
if(path.size() == nums.size()){
result.push_back(path);
return;
}
for(int i = 0;i < nums.size();i++){
if(used[i] == false){
path.push_back(nums[i]);
used[i] = true;
backtracking(nums,used);
path.pop_back();
used[i] = false;
}
}
}
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<bool> used(nums.size(),false);
backtracking(nums,used);
return result;
}
};
三、全排列||
1.题目
2.思路
这道题目和46.全排列 (opens new window)的区别在与给定一个可包含重复数字的序列,要返回所有不重复的全排列。
这里又涉及到去重了。去重一定要对元素进行排序!!!
以示例中的 [1,1,2]为例,去重过程如图:
同一树层,前一位(也就是nums[i-1])如果使用过,那么就进行去重。
组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果。
总体代码:
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& nums,vector<bool>& 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] == false)) continue;
if(used[i] == false){
path.push_back(nums[i]);
used[i] = true;
backtracking(nums,used);
path.pop_back();
used[i] = false;
}
}
}
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<bool> used(nums.size(),false);
backtracking(nums,used);
return result;
}
};