题目链接:491. 递增子序列
1.思路
与前面的去重不同这个去重不能改变顺序,所以不能进行排序,所以需要另外1一种去重方法,还需要判断递增,和组合总和一样我们可以边递归边比较
2.回溯法
class Solution {
public:
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>uset;
for (int i = startindex; i < nums.size(); i++) {
if (!path.empty() && nums[i] < path.back() || uset.find(nums[i]) != uset.end()) continue;
path.push_back(nums[i]);
uset.insert(nums[i]);
backtracking(nums, i + 1);
path.pop_back();
}
}
vector<vector<int>> findSubsequences(vector<int>& nums) {
backtracking(nums, 0);
return result;
}
};
3.递归三部曲
1.确定递归函数参数
除了需提供的哪些参数外,还需要startindex避免重复。
2.确定终止条件
根据题目结果集的元素个数小于2时就不符合条件,所以return前需要判断长度,
3.确定单层搜索的逻辑
一.可以看出我们树层遍历再次遇到相同的点,根据这个点进行树枝遍历时后面都会相同,所以我们需要跳过该点
二.怎么判断用没有用过,因为不能排序所以不能用used数组,而且只需要判断这一层有没有用过就可以了,所以可以在没一层设置一个set数组,当遍历这一层时判断set数组有没有记录这个数,没记录才能加入这个数
三.怎么判断顺序,当我们数字遍历时,我们就可以判断当前需要加入的点和path数组头结点的大小,错了就进行树层遍历,边递归边遍历
4.可以在for循环上面定义uset,因为只是判断层次的相同,每一层设置一个
经过以前的经历我们可以看出有两种去重的方法
二刷解析
!path.empty() && nums[i] < path.back() || uset.find(nums[i]) != uset.end()
1.如果这一层这一个不符合递增序列就判断这一层的下一个符不符合,continue
2.相等的话就判断当前的这一个是不是层遍历,层遍历的话后面枝遍历的数值加入是一样的,所以也要判断这一层的下一个符不符合
3.前提是path不能为空,因为这个语句是顺序执行的,所以这个语句放到前面就可以很好的避免错误了
题目链接:46. 全排列
1.思路
和组合问题和子集问题不相同的是,这个是可以返回到以前遍历的数字的,只需要顺序不同就可以了
2.回溯法
class Solution {
public:
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]) {
path.push_back(nums[i]);
} else {
continue;
}
used[i] = true;
backtracking(nums, used);
used[i] = false;
path.pop_back();
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<bool> used(nums.size(), false);
backtracking(nums, used);
return result;
}
};
三.递归三部曲
1.确定递归函数参数,没有startindex,因为里面的元素可以重复,只要顺序不同就行了,但是树枝遍历时必须判断当前结点是否在上几层遍历了没,所以需要定义一个used数组
2.确定终止条件,不能再中间树枝停止,只有长度相等才会停止,必须要在叶子停止
3.确定单层搜索的逻辑,用过就会标记,回溯返回上一层标记消失,再递归又能遍历到这一个结点就能有效的发生全排列
题目链接:47. 全排列 II
1.思路
这一题需要数组中的元素可以重复,但是path数组不能重复,说白了还是和上次一样,树层遍历不能重复加入相同的元素
两种方法:
1.used
2.uset
2.回溯法
class Solution {
public:
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;
}
unordered_set<int>uset;
for (int i = 0; i < nums.size(); i++) {
if (used[i] == true || uset.find(nums[i]) != uset.end()) {
continue;
}else {
path.push_back(nums[i]);
}
uset.insert(nums[i]);
used[i] = true;
backtracking(nums, used);
used[i] = false;
path.pop_back();
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<bool>used(nums.size(), false);
backtracking(nums, used);
return result;
}
};
3.回溯三部曲
和第一题一样,但是这题是全排列,我们排序完也是一样的,所以可以用used
二刷解析
used[i] == true || uset.find(nums[i]) != uset.end()
1.用过
2.这一层用过这个数字,为什么前面不加上path.size() > 0呢,因为如果为0就不会被发现,多此一举。还是正确的