题目:491.递增子序列
给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中至少有两个元素。你可以按任意顺序返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

题目链接/文章讲解
https://programmercarl.com/0491.%E9%80%92%E5%A2%9E%E5%AD%90%E5%BA%8F%E5%88%97.html
视频讲解:https://www.bilibili.com/video/BV1EG4y1h78v
思路
三步骤
1、函数的输入和返回值。返回值为void,输入有数组nums,同时因为求得是子序列,不能对同一个元素重复使用,输入还要有int类型的startIndex来控制位置。
2、函数的终止条件:
对于求子集之类的题,可以不用加终止条件。
本题有一个限制条件,就是子序列中至少有两个元素,所以要进行限定。
3、单层搜索的逻辑
给了一个数组,而且这个数组中有重复的元素,要求我们找出不同的递增子序列。
因此肯定需要去重。之前都是借助了一个元素都是bool类型的数组used数组来实现去重,这个不行,因为之前使用used数组之前,都对原始的输入数组进行了排序,这道题不能排序。
可以使用set数组来实现去重。我们把每一次取出的数都同步的放入set中。后面取出的数,都先与set里面的比较,如果有重复就不取。同时如果后面的数小于前面的数,则不符合递增的要求,也不取。

我们去重的对象是同一父节点下的同层。
解题
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(const vector<int>& nums, int startIndex) {
if (path.size() >
1) // 不加这个条件,会把空集和一个元素的集合也放到result中
result.push_back(
path); // 只要path里的元素大于1,就证明符合条件,就可以放到result中
unordered_set<int> uset; // 用set来去重,每一层递归都会重新定义uset
for (int i = startIndex; i < nums.size(); i++) {
if (!path.empty() && nums[i] < path.back() ||
uset.find(nums[i]) !=
uset.end()) { // path.back():获得path数组中的最后一个元素
continue; // 跳过当前的for循环,进入下一个
}
uset.insert(nums[i]); // 用来去重,将每一层取得数都放到set里面
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 ,返回其所有可能的全排列。你可以按任意顺序返回答案。

题目链接/文章讲解:
https://programmercarl.com/0046.%E5%85%A8%E6%8E%92%E5%88%97.html
视频讲解:https://www.bilibili.com/video/BV19v4y1S79W
思路
排列问题。
排列问题和组合问题最大的区别是,排列是有序的。[1,2]和[2,1]是不同的排列,所以每一次都要从头取数。
因此排列问题不需要startIndex。
回溯三步骤。
1、函数的返回值和参数。返回值为void,参数是数组nums,还需要一个参数used,来记录同个树枝取过的数。
2、函数终止条件。

叶子节点,就是收割结果的地方。
当收集元素的数组path的大小达到和nums数组一样大的时候,说明找到了一个全排列,也表示到达了叶子节点。
3、单次搜索的逻辑。
used[i]为true,说明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 ,按任意顺序返回所有不重复的全排列。

题目链接/文章讲解
https://programmercarl.com/0047.%E5%85%A8%E6%8E%92%E5%88%97II.html
视频讲解:https://www.bilibili.com/video/BV1R84y1i7Tm
思路
本题与上题相比,不同的地方在于本题的数组里有重复的元素,需要去重。
是40.组合总和II 和46.全排列的结合。
解题
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(const 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);
used[i] = false;
path.pop_back();
}
}
}
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
result.clear();
path.clear();
vector<bool> used(nums.size(), false);
sort(nums.begin(), nums.end());
backtracking(nums, used);
return result;
}
};
文章讲述了如何使用回溯法解决两个相关问题:一是寻找给定整数数组中的不同递增子序列,要求至少包含两个元素;二是处理全排列问题,包括无重复数字的全排列和可包含重复数字的全排列,通过去重实现不重复的排列结果。

1016

被折叠的 条评论
为什么被折叠?



