491.递增子序列
题目链接:491.递增子序列
难度指数:😀😐😕
有些题解说这道题是深搜。其实所有的回溯算法,你都可以说它是深搜;所有的深搜,你都可以说它是递归;
本题,我们做一个更细致的分类,认为它还是属于回溯算法。(当然,你说是深搜也没毛病)
和子集问题有点像,但又处处是陷阱
本题和刚做过的 90.子集Ⅱ 非常像,但又很不一样,很容易掉坑里。
😮本题是求自增子序列,❌不可以对原数组进行排序。
⚠️不能使用之前的去重逻辑!!
依旧是每个节点都是我们要收集的结果。
树形结构:
代码思路:
去重做的是树层上的去重;
树枝上去取不同的元素,这些元素有可能数值相同。(即树枝上可以取相同数值,没毛病 )
vector<vector<int>> result; //存放子集的结果集
vector<int> path; //存放子集
void backtracking(vector<int> nums, int startIndex) {
//(可以不用终止条件)
//要求递增子序列的大小至少为2
if (path.size() > 1) {
result.push_back(path);
//注意这里不要return,要取树上的节点
}
//单层搜索的逻辑
unordered_set<int> uset; //使用set对本层元素进行去重
for (int i = startIndex; i < nums.size(); i++) {
//树层去重操作
if ((!path.empty() && nums[i] < path.back()) || uset.find(nums[i]) != uset.end()) {
continue;
}
uset.insert(nums[i]); //记录这个元素在本层用过了,本层后面不能再用
path.push_back(nums[i]);
backtracking(nums, i + 1); //递归
path.pop_back();
}
}
AC代码: (核心代码模式)
class Solution {
private:
vector<vector<int>> result; //存放子集的结果集
vector<int> path; //存放子集
void backtracking(vector<int>& nums, int startIndex) {
//(不用写终止条件)
//要求递增序列的大小至少为2
if (path.size() > 1) {
result.push_back(path);
//注意这里不要return,要取树上的节点
}
//单层搜索的逻辑
unordered_set<int> uset; //使用set对本层元素进行去重
for (int i = startIndex; i < nums.size(); i++) {
if ((!path.empty() && nums[i] < path.back()) || uset.find(nums[i]) != uset.end()) {
continue;
}
uset.insert(nums[i]); //记录这个元素在本层用过了,本层后面不能再用
path.push_back(nums[i]);
backtracking(nums, i + 1);
path.pop_back();
}
}
public:
vector<vector<int>> findSubsequences(vector<int>& nums) {
result.clear();
path.clear();
backtracking(nums, 0);
return result;
}
};
哈希优化:
46.全排列
题目链接:46.全排列
难度指数:😀😐🤨
本题重点感受一下,排列问题
与 组合问题
,组合总和
,子集问题
的区别。为什么排列问题不用 startIndex
处理排列问题就不用使用 startIndex
了🤗。
代码思路:
因为排列问题,每次都要从头开始搜索,例如元素1在[1,2]中已经使用过了,但是在[2,1]中还要再使用一次1。
而used数组,其实就是记录此时path里都有哪些元素使用了,一个排列里一个元素只能使用一次。
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] == true) {
continue;
}
used[i] = true;
path.push_back(nums[i]);
backtracking(nums, used); //回溯
path.pop_back();
used[i] = false;
}
}
AC代码: (核心代码模式)
class Solution {
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] == true) { //path里已经收录的元素,直接跳过
continue;
}
used[i] = true;
path.push_back(nums[i]);
backtracking(nums, used); //递归
path.pop_back(); //回溯
used[i] = false;
}
}
public:
vector<vector<int>> permute(vector<int>& nums) {
result.clear();
path.clear();
vector<bool> used(nums.size(), false);
backtracking(nums, used);
return result;
}
};
47.全排列Ⅱ
题目链接:47.全排列Ⅱ
难度指数:😀😐🤨
本题 就是我们讲过的 40.组合总和II 去重逻辑 和 46.全排列 的结合,可以先自己做一下,然后重点看一下 文章中 我讲的拓展内容。 used[i - 1] == true 也行,used[i - 1] == false 也行
代码思路:
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) {
used[i] == true;
path.push_back(nums[i]);
backtracking(nums, used); //递归
path.pop_back(); //回溯
used[i] = false;
}
}
}
AC代码: (核心代码模式)
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) {
used[i] = true;
path.push_back(nums[i]);
backtracking(nums, used); //递归
path.pop_back(); //回溯
used[i] = false;
}
}
}
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
result.clear();
path.clear();
sort(nums.begin(), nums.end()); //排序
vector<bool> used(nums.size(), false);
backtracking(nums, used);
return result;
}
};