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]] 提示:
- 1 <= nums.length <= 15
- -100 <= nums[i] <= 100
思路
- 定义数据结构:
- result:用于存储所有找到的递增子序列。
- current:用于存储当前正在构建的递增子序列。
- 回溯函数:该函数应从数组的某个位置开始,在回溯的每一步中做出选择(添加一个数到当前递增子序列或不添加),然后进入下一层递归。
- 结束条件:如果当前的递增子序列长度大于等于2,将其添加到结果中。
- 去重:在同一递归层次中,如果一个数已经被选择过,那么就跳过这个数,以避免产生重复的子序列。
题解
class Solution {
private:
vector<vector<int>> result;
vector<int> current;
public:
void backtracking(vector<int>& nums, int start) {
if (current.size() >= 2) {
result.push_back(current);
}
unordered_set<int> seen;
for (int i = start; i < nums.size(); ++i) {
if ((current.empty() || nums[i] >= current.back()) && seen.find(nums[i]) == seen.end()) {
current.push_back(nums[i]); // 选择
backtracking(nums, i + 1); // 回溯
current.pop_back(); // 撤销选择
seen.insert(nums[i]); // 标记为已选择
}
}
}
vector<vector<int>> findSubsequences(vector<int>& nums) {
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]]
提示:
- 1 <= nums.length <= 6
- -10 <= nums[i] <= 10
- nums 中的所有整数 互不相同
思路
在排列问题中,每次递归调用都可以从整个数组中选择元素,而不仅仅是从某个起始索引开始。这是因为排列问题需要考虑数组中所有元素的所有可能顺序,而不是仅考虑从某个点开始的子数组。
与组合问题不同,在组合问题中,我们通常使用 startIndex 来避免生成重复的组合,因为组合 [1, 2] 和 [2, 1] 被认为是相同的。但在排列问题中,[1, 2] 和 [2, 1] 是两个不同的排列。
因此,在排列问题中,我们通常使用一个额外的数据结构(如集合或布尔数组)来跟踪哪些元素已经被添加到当前排列中,而不是通过 startIndex 来限制可选元素的范围。
题解
class Solution {
private:
vector<vector<int>> result;
vector<int> current;
public:
void backtracking(vector<int>& nums, unordered_set<int>& seen) {
if (current.size() == nums.size()) {
result.push_back(current);
return;
}
for (int i = 0; i < nums.size(); ++i) {
if (seen.find(nums[i]) == seen.end()) {
current.push_back(nums[i]); // 选择
seen.insert(nums[i]); // 标记为已选择
backtracking(nums, seen); // 回溯
seen.erase(nums[i]); // 取消标记
current.pop_back(); // 撤销选择
}
}
}
vector<vector<int>> permute(vector<int>& nums) {
unordered_set<int> seen;
backtracking(nums, seen);
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]]
提示:
- 1 <= nums.length <= 8
- -10 <= nums[i] <= 10
思路
- 排序数组:首先,对数组进行排序。这样,所有的相同数字都会相邻出现,更容易进行去重操作。
- 使用额外数据结构:使用一个布尔数组 used 来跟踪哪些数字已经被添加到当前排列中。
- 去重:在同一递归层次中,如果一个数和它前面的数相同,并且前面的数已经被使用过,那么就跳过这个数。
题解
class Solution {
private:
vector<vector<int>> result;
vector<int> current;
public:
void backtracking(vector<int>& nums, vector<bool>& used) {
if (current.size() == nums.size()) {
result.push_back(current);
return;
}
for (int i = 0; i < nums.size(); ++i) {
if (used[i]) continue;
// 去重:跳过在同一递归层次中相同的元素
if (i > 0 && nums[i] == nums[i-1] && !used[i-1]) continue;
current.push_back(nums[i]); // 选择
used[i] = true; // 标记为已选择
backtracking(nums, used); // 回溯
used[i] = false; // 取消标记
current.pop_back(); // 撤销选择
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end()); // 排序
vector<bool> used(nums.size(), false);
backtracking(nums, used);
return result;
}
};