第七章 回溯算法 part05
1.LeetCode. 递增子序列
1.1题目链接:491.递增子序列
文章讲解:代码随想录
视频讲解:B站卡哥视频
1.2思路:这个递增子序列比较像是取有序的子集。而且本题也要求不能有相同的递增子序列。这又是子集,又是去重,是不是不由自主的想起了刚刚讲过的90.子集II。就是因为太像了,更要注意差别所在,要不就掉坑里了!在90.子集II (opens new window)中我们是通过排序,再加一个标记数组来达到去重的目的。而本题求自增子序列,是不能对原数组进行排序的,排完序的数组都是自增子序列了。所以不能使用之前的去重逻辑!
为了有鲜明的对比,我用[4, 7, 6, 7]这个数组来举例,抽象为树形结构如图:
1.3附加代码如下所示:
class Solution {
public: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();}}vector<vector<int>> findSubsequences(vector<int>& nums) {result.clear();path.clear();backtracking(nums,0);return result;}
};
2.LeetCode. 全排列
2.1题目链接:46.全排列
文章讲解:代码随想录
视频讲解:B站卡哥视频
2.2思路:首先排列是有序的,也就是说 [1,2] 和 [2,1] 是两个集合,这和之前分析的子集以及组合所不同的地方。可以看出元素1在[1,2]中已经使用过了,但是在[2,1]中还要在使用一次1,所以处理排列问题就不用使用startIndex了。但排列问题需要一个used数组,标记已经选择的元素,如图橘黄色部分所示:
2.3附加代码如下所示:
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里已经收录的元素,直接跳过path.push_back(nums[i]);used[i]=true;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(),0);backtracking(nums,used);return result;}
};
3.LeetCode.全排列 II
3.1题目链接:47.全排列 II
文章讲解:代码随想录
视频讲解:B站卡哥视频
3.2思路:这道题目和46.全排列 (opens new window)的区别在与给定一个可包含重复数字的序列,要返回所有不重复的全排列。这里又涉及到去重了。在40.组合总和II (opens new window)、90.子集II (opens new window)我们分别详细讲解了组合问题和子集问题如何去重。那么排列问题其实也是一样的套路。还要强调的是去重一定要对元素进行排序,这样我们才方便通过相邻的节点来判断是否重复使用了。
以示例中的 [1,1,2]为例 (为了方便举例,已经排序)抽象为一棵树,去重过程如图:
3.3附加代码如下所示:
//树层不可以重复取值树枝可以重复取值
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++){// used[i - 1] == true,说明同一树枝nums[i - 1]使用过// used[i - 1] == false,说明同一树层nums[i - 1]使用过// 如果同一树层nums[i - 1]使用过则直接跳过if(i>0&&nums[i-1]==nums[i]&&used[i-1]==false)continue;//去重要进行树层去重树枝不去重,used[i-1]==false表明这是同一个树层if(used[i]==true)continue;//同一树枝中去过的数不能再取值了path.push_back(nums[i]);used[i]=true;backtracking(nums,used);path.pop_back();used[i]=false;}}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;}
};