算法学习Day29递增子序列、全排列
Day29递增子序列、全排列
By HQWQF 2024/01/16
笔记
491.递增子序列
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。
示例:
- 输入: [4, 6, 7, 7]
- 输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]
说明:
- 给定数组的长度不会超过15。
- 数组中的整数范围是 [-100,100]。
- 给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况
回溯法+哈希表
捕捉到信息点:
1.元素不能复用
2.有重复元素。要去重
有了上一题的经验我们容易写出这样的代码:
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);}
if (path.size() == nums.size()) {
return;
}
for (int i = startIndex; i < nums.size(); i++) {
// 要对同一树层使用过的元素进行跳过
if (i > startIndex && nums[i] == nums[i - 1]) {
continue;
}
if(path.size() > 0 && nums[i] < path[path.size()-1]){continue;}
path.push_back(nums[i]);
backtracking(nums,i + 1);
path.pop_back();
}
}
public:
vector<vector<int>> findSubsequences(vector<int>& nums) {
path.clear();
result.clear();
// 首先把给nums排序,让其相同的元素都挨在一起。
backtracking(nums, 0);
return result;
}
};
然而这样的代码是错误的,因为我们要得出递增子序列所以不能将源序列排序,而如果不排序,nums[i] == nums[i - 1]就无法跳过重复元素了。
我们可以所以哈希表来去重:
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);
// 注意这里不要加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;
}
};
另外可以发现题目数组中的整数范围是 [-100,100]。既然范围不大,我们可以直接使用数组来作为哈希表的底层:
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);
// 注意这里不要加return,要取树上的节点
}
int uset[201] = {0}; // 使用set对本层元素进行去重, [-100,100]有201个数
for (int i = startIndex; i < nums.size(); i++) {
if ((!path.empty() && nums[i] < path.back())
|| uset[100 + nums[i]] != 0) {
continue;
}
uset[100 + nums[i]] = 1; // 记录这个元素在本层用过了,本层后面不能再用了
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.全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入: nums = [1,2,3]输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
输入: nums = [0,1]输出:[[0,1],[1,0]]
回溯法
捕捉到两个信息点:
1.终止条件和加入解集时机是path达到一定长度
2.排列,意味着不同顺序的数组也视为不同
3.元素不能重复利用
4.不包含重复元素
我们回想一下之前的题目中startIndex的作用:
1.给下一层的startIndex赋值i,以让同层的其他节点的后续节点不会出现[不同顺序但组成元素相同]的path
2.在第一点的基础上给下一层的startIndex赋值i + 1,避免重复利用元素
我们发现[不同顺序但组成元素相同]这个特性与我们需要的全排列想矛盾,但是避免重复利用元素又是我们需要的,说明我们不能使用startIndex而需要使用其他方法避免重复利用元素
我们可以回想到曾经在组合总和2中曾经用过useing数组来确定某个元素是否在当前的path中,我们也可以这样做,只要某个元素没出现在当前的path中,就是我们当前可以选择的元素。
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] == true) continue; // path里已经收录的元素,直接跳过
used[i] = true;
path.push_back(nums[i]);
backtracking(nums, used);
path.pop_back();
used[i] = false;
}
}
vector<vector<int>> permute(vector<int>& nums) {
result.clear();
path.clear();
vector<bool> used(nums.size(), false);
backtracking(nums, used);
return result;
}
};
47.全排列 II
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
输入: nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
示例 2:
输入: nums = [1,2,3]输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
回溯法
和上一题的差别在包含重复元素,因此我们需要去重。
我们发现这题的源序列的顺序不影响结果,所以我们直接用基于排序的去重。
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
void backtracking (vector<int>& nums, vector<bool>& useing) {
// 此时说明找到了一组
if (path.size() == nums.size()) {
result.push_back(path);
return;
}
for (int i = 0; i < nums.size(); i++) {
if (useing[i] == true) continue; // path里已经收录的元素,直接跳过
if (i > 0 && nums[i] == nums[i - 1]&& useing[i - 1] == false) {
continue;
}
useing[i] = true;
path.push_back(nums[i]);
backtracking(nums, useing);
path.pop_back();
useing[i] = false;
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
result.clear();
path.clear();
vector<bool> useing(nums.size(), false);
sort(nums.begin(), nums.end());
backtracking(nums, useing);
return result;
}
};

浙公网安备 33010602011771号