代码随想录算法训练营第二十九天| 491 递增子序列 46 全排列 47 全排列 II
LeetCode 491 递增子序列
题目链接: 491.递增子序列
思路:本题不能先进行排序,一旦排序即为自增序列,违背题意。
回溯三部曲
- 回溯函数模板返回值及参数
本题使用startIndex来调整下一层递归的其实位置
vector<string> result;
vector<int> path;
void backtracking(vector<int>& nums, int startIndex)
- 回溯终止条件
本题要求完全遍历,可以不加终止条件
if(path.size() > 1) {
result.push_back(path);
//此处不能加return,要遍历完树上所有节点
}
- 单层搜索逻辑
同一父节点下的同层使用过的元素不能再使用
unordered_set<int> uset; //使用set来进行去重
for(int i = startIndex; i < nums.size(); i++) {
//如果path中有元素且遍历元素不满足递增或者没在uset中出现过
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(); //回溯
}
完整代码:
class Solution {
private:
vector<vector<int> > result;
vectoy<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;
uset.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;
}
};
此题可以考虑用数组做哈希
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);
int used[201] = {0}; //题目限制数值范围为[-100, 100]
for(int i = startIndex; i < nums.size(); i++) {
if((!path.empty() && nums[i] < path.back()) || used[nums[i] + 100] == 1) continue;
path.push_back(nums[i]);
used[nums[i] + 100] = 1;
backtracking(nums, i + 1);
path.pop_back();
}
}
public:
vector<vector<int> > findSubsequences(vector<int>& nums) {
backtracking(nums, 0);
return result;
}
};
LeetCode 46 全排列
题目链接: 46.全排列
思路:排列问题和组合问题不同,其顺序不同子集也不同,所以本题不需要startIndex,但是需要used数组用以判断元素是否使用过
- 递归函数返回值及参数
vector<string> result;
vector<int> path;
void backtracking(vector<int>& nums, vector<bool> used)
- 回溯终止条件
当patn大小和nums大小一样时,说明找到了一个全排列
if(path.size == nums.size()) {
result.push_back(path);
return;
}
- 单层搜索逻辑
for(int i = 0; i < nums.size(); i++) {
if(used[i] == true) continue; //path里已有的元素,跳过
used[i] = true;
path.push_back(nums[i]);
backtracking(nums, used);
path.pop_back();
used[i] == false;
}
整体代码为:
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] == true) continue;
used[i] = true;
path.push_back(nums[i]);
backtracking(nums, used);
used[i] = false;
path.pop_back();
}
}
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<bool> used(nums.size(), false);
backtracking(nums, used);
return result;
}
};
LeetCode 47 全排列 II
题目链接: 47.全排列 II
思路:和之前数组中有重复元素求子集的例子相似
- 递归函数及返回参数
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] || used[i]) continue;
path.push_back(nums[i]);
used[i] = true;
backtracking(nums, used);
used[i] = false;
path.pop_back();
}
整体代码为:
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] || used[i]) continue;
path.push_back(nums[i]);
used[i] = true;
backtracking(nums, used);
used[i] = false;
path.pop_back();
}
}
public:
vector<vector<int> > permuteUnique(vector<int>& nums) {
vector<bool> used(nums.size(), false);
sort(nums.begin(), nums.end());
backtracking(nums, used);
return result;
}
};
注:此题既可以用used[i - 1] == false
进行树层去重,也可以用used[i - 1] == true
进行树枝去重,但后者比前者多做了很多无用操作。