491非递减子序列
力扣题目链接
题目描述:
给你一个整数数组 nums
,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
示例 1:
输入:nums = [4,6,7,7] 输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]
示例 2:
输入:nums = [4,4,3,2,1] 输出:[[4,4]]
代码:
用哈希表
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;
}
used[nums[i] + 100] = 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;
}
};
不用哈希表,用set
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;
}
};
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]]
示例 3:
输入:nums = [1] 输出:[[1]]
代码:
class Solution {
public:
// 存储所有不重复的全排列结果
vector<vector<int>> result;
// 当前构建的全排列路径
vector<int> path;
// 回溯函数
void backtracking(vector<int> nums, vector<bool> &used) {
// 基本情况:当路径的长度与 nums 的长度相等时,说明现有排列已完整
if (path.size() == nums.size()) {
result.push_back(path); // 将当前排列加入结果集中
return; // 结束当前递归
}
// 遍历所有元素,用于构建全排列
for (int i = 0; i < nums.size(); i++) {
// 如果当前元素未被使用
if (used[i] == false) {
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全排列||
题目题目链接
题目描述:
给定一个可包含重复数字的序列 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 {
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++) {
// used[i - 1] == true,说明同一树枝nums[i - 1]使用过
// used[i - 1] == false,说明同一树层nums[i - 1]使用过
// 如果同一树层nums[i - 1]使用过则直接跳过
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;
}
};
-
去重的核心逻辑:
- 这里的去重逻辑比较巧妙:
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) { continue; }
i > 0
:确保不是第一个元素。nums[i] == nums[i - 1]
:检查当前元素和前一个元素是否相同(即为重复)。used[i - 1] == false
:确保只有在前一个相同元素未被使用时,当前元素才会被考虑。所以只在使用前一个相同元素的情况下,才会使用当前元素,避免生成重复的排列。
- 这里的去重逻辑比较巧妙:
意思就是,在已经确定了第二个元素时(也就是当前元素是true)而前一个元素是false时说明前一个元素肯定已经考虑过了,因为遍历是从左往右遍历,此时如果不进行continue,就会重复考虑相同元素,所以当used[i - 1] == false
:时就cotinue
-
标记使用状态:
- 在选择某个元素之后,将其标记为已使用:
used[i] = true; path.push_back(nums[i]);
- 进入递归调用,生成剩下的排列。
- 递归完成后,回退状态:
path.pop_back(); used[i] = false;
- 在选择某个元素之后,将其标记为已使用:
-
主函数
permuteUnique
:- 初始化
result
和path
,并创建一个布尔数组used
来跟踪元素使用状态,调用backtracking
函数。
- 初始化