题目: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来去重,每一层递归都会重新定义usetfor (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;}
};