一、LeetCode 491.递增子序列
题目链接/文章讲解/视频讲解:https://programmercarl.com/0491.%E9%80%92%E5%A2%9E%E5%AD%90%E5%BA%8F%E5%88%97.html
状态:已解决
1.思路
这道题看似和90题差不多,都是求子集并且有重复元素,但实则大有不同。因为这道题要求给出非递减子序列,因此我们不能用排序的方式来去重。那该怎么办呢?其实40题的去重逻辑的核心就是要我们在同层递归(树层)的时候,不再选取已经出现过的值。
在40题中,我们用nums[i]==nums[i-1]和used[i-1]==0两个共同的条件来判定此时是树层重复还是树枝重复。那失去了nums[i]有序的这个条件又如何判定了?其实很简单,因为我们只需要控制同一树层的时候,不选取已经选取过的值,也就是说,只要我们在树层开始前设个数组,让这个数组存放已经出现过的值,那么如果这次for循环选取的值数组中已经有了,就说明该值已经出现过,我们跳过不做后续操作就好。(做完了发现可行并且老师也是这个做法!!)
2.代码实现
每层递归用来存放for循环已经选取过的值的数组可以采用set实现,也可以用map实现。我这里用的map,老师讲解用的set。
class Solution {
public:vector<int> path;vector<vector<int>> result;void backtracking(vector<int>& nums,int startIndex){if(path.size()>1)//注意集合中至少两个元素result.push_back(path);if(startIndex>=nums.size()){return ;}unordered_map<int,int> used;for(int i=startIndex;i<nums.size();i++){if(i>startIndex && used.find(nums[i])!= used.end() && used[nums[i]]==1){continue;}//判断是否已经出现过该值,used.find(nums[i])!= used.end()很重要if(path.size()==0 || nums[i]>=path[path.size()-1]){path.push_back(nums[i]);used[nums[i]]=1;backtracking(nums,i+1);path.pop_back();}}}vector<vector<int>> findSubsequences(vector<int>& nums) {path.clear();result.clear();backtracking(nums,0);return result;}
};
二、46.全排列
题目链接/文章讲解/视频讲解:https://programmercarl.com/0046.%E5%85%A8%E6%8E%92%E5%88%97.html
状态:已解决
1.思路
排列与组合的区别在于排列要看集合元素之间的顺序,而组合不看,也就是说,对排列来说,{1,2}和{2,1}是两个集合,而对组合而言,二者是同一个集合。我们在做组合题时曾经说过,为了避免组合时出现相同组合{1,2}、{2,1},令startIndex根据上层i的取值来取,使得startIndex>i,因此,要使组合变排列,只需将startIndex的取值范围变一下,变为从0开始取值,确保原数组后面的元素也可以作为集合中靠前的元素。不过,为了确保前后取到同一个元素值,我们需要记录下来每层递归所取的值,让后续递归不再取它们,也就是要进行树枝去重,用used数组实现。
2.完整代码
class Solution {
public:vector<int> path;vector<vector<int>> result;void backtracking(vector<int>& nums,vector<int>& used){if(path.size() == nums.size()){result.push_back(path);return ;}for(int i=0;i<nums.size();i++){if(used[i]==1) continue;path.push_back(nums[i]);used[i] = 1;backtracking(nums,used);used[i] = 0;path.pop_back();}}vector<vector<int>> permute(vector<int>& nums) {vector<int> used(nums.size(),0);backtracking(nums,used);return result;}
};
三、47.全排列 II
题目链接/文章讲解/视频讲解:https://programmercarl.com/0047.%E5%85%A8%E6%8E%92%E5%88%97II.html
状态:已解决
1.思路
因为有重复元素,又是排列题,故此题实则就是40题双重去重和46题的一个结合。需要在46题一个树枝去重和一个树层去重的操作,具体怎么操作可以去看40题的解法。注意,对于used操作的用法,在组合和排序中,used数组含义是相同的,但是组合主要是用来佐证确定树层去重,而排序是用来确保树枝去重的。
2.代码实现
class Solution {
public:vector<int> path;vector<vector<int>> result;void backtracking(vector<int>& nums,vector<int>& 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]==0) continue;if(used[i]==1) continue;path.push_back(nums[i]);used[i]=1;backtracking(nums,used);used[i]=0;path.pop_back();}}vector<vector<int>> permuteUnique(vector<int>& nums) {sort(nums.begin(),nums.end());vector<int> used(nums.size(),0);backtracking(nums,used);return result;}
};