491.递增子序列
链接:代码随想录
视频链接:https://www.bilibili.com/video/BV1EG4y1h78v
错误回答:
class Solution { public: // 数组中可能含有重复元素, //如出现两个整数相等,也可以视作递增序列的一种特殊情况 //子序列:不需要连续 //回溯的条件里加上要比上一层传过来的元素大 //不需要排序 //为了避免出现[4,7(第一个7)],[4,7(第二个7)]7这个数字只用第一个 vector<vector<int>>v; vector<int>mv; vector<vector<int>> findSubsequences(vector<int>& nums) { backtracing(nums,0); return v; } void backtracing(vector<int> &nums,int startIndex) { if(mv.size()>=2) { v.push_back(mv); } for(int j=startIndex;j<nums.size();j++) { if(j>startIndex &&nums[j]==nums[j-1]) { continue; } if(nums[j]>=nums[startIndex])//保证递增 { mv.push_back(nums[j]); backtracing(nums,j+1); mv.pop_back(); } } } };
猜想:
这部分只能横向的约束递归树,无法纵向的约束。
修改条件,希望本层回溯节点必须比上层回溯节点值大
void backtracing(vector<int> &nums,int startIndex) { if(mv.size()>=2) { v.push_back(mv); } for(int j=startIndex;j<nums.size();j++) { if(j>startIndex &&nums[j]==nums[j-1]) { continue; } if(startIndex>=1 && nums[j]>=nums[startIndex-1])//保证递增 { mv.push_back(nums[j]); backtracing(nums,j+1); mv.pop_back(); } if(startIndex==0) { mv.push_back(nums[j]); backtracing(nums,j+1); mv.pop_back(); } } }
仍然不对,继续修改。pre=-101//记录上层回溯节点的值,本层回溯节点必须比上层回溯节点值大
class Solution { public: // 数组中可能含有重复元素, //如出现两个整数相等,也可以视作递增序列的一种特殊情况 //子序列:不需要连续 //回溯的条件里加上要比上一层传过来的元素大 //不需要排序 //为了避免出现[4,7(第一个7)],[4,7(第二个7)]7这个数字只用第一个 vector<vector<int>>v; vector<int>mv; int pre=-101;//记录上层回溯节点的值,本层回溯节点必须比上层回溯节点值大 vector<vector<int>> findSubsequences(vector<int>& nums) { backtracing(nums,0,pre); return v; } void backtracing(vector<int> &nums,int startIndex,int pre) { if(mv.size()>=2) { v.push_back(mv); } for(int j=startIndex;j<nums.size();j++) { if(j>startIndex &&nums[j]==nums[j-1]) { continue; } if(nums[j]>=pre)//保证递增 { mv.push_back(nums[j]); int temp=pre; pre=nums[j]; backtracing(nums,j+1,pre); pre=temp; mv.pop_back(); } } } };
结果仍然不对。
去看了视频,发现我自己的横向去重部分的代码,还是建立在数组排序之上的,而这里给出的原数组根本没有排序,也就是说用
根本行不通。
老师这里用的是每一节点扩展的横向选择都重新建立一个unordered_set,从而对横向的进行去重。竖向的数枝和我想像的一样,不用去重。
另外,记录上一层的结果也不用像我一样单独加一个pre参数,而是使用了已经记录树枝路径的最后一个元素,也就是mv.back()去比较记录。用本层的nums[j]和mv.back ()进行比较。
结果:
class Solution { public: vector<vector<int>>v; vector<int>mv; vector<vector<int>> findSubsequences(vector<int>& nums) { backtracing(nums,0); return v; } void backtracing(vector<int> &nums,int startIndex) { if(mv.size()>=2) { v.push_back(mv); } //对于每一个要横向扩展的节点,新建unoredered_set去重 unordered_set<int>uset; for(int j=startIndex;j<nums.size();j++) { if(uset.find(nums[j])!=uset.end())//如果是重复的元素,跳过 { continue; } if(!mv.empty()&& nums[j]<mv.back())//要加入的元素小于已有元素的最后一个元素,则不加 { continue; } uset.insert(nums[j]); mv.push_back(nums[j]); backtracing(nums,j+1); mv.pop_back(); } } };
46.全排列
本题重点感受一下,排列问题 与 组合问题,组合总和,子集问题的区别。 为什么排列问题不用 startIndex
链接:代码随想录
终于轮到了排列!!
排列问题,每次都要从头开始搜索,例如元素1在[1,2]中已经使用过了,但是在[2,1]中还要再使用一次1。
而used数组,其实就是记录此时path里都有哪些元素使用了,一个排列里一个元素只能使用一次。
class Solution { public: // 背模板还是好背的 vector<vector<int>>v; vector<int>mv; vector<bool>used; vector<vector<int>> permute(vector<int>& nums) { used.resize(nums.size(),false); backtracing(nums); /* for(auto p:used) { cout<<p<<" "; } cout<<endl;*/ return v; } void backtracing(vector<int> &nums) { if(mv.size()==nums.size()) { v.push_back(mv); } for(int j=0;j<nums.size();j++) { if(!used[j]) { used[j]=true; mv.push_back(nums[j]); backtracing(nums); mv.pop_back(); used[j]=false; } } } };
47.全排列 II
链接:代码随想录
本题 就是我们讲过的 40.组合总和II 去重逻辑 和 46.全排列 的结合,可以先自己做一下,然后重点看一下 文章中 我讲的拓展内容。 used[i - 1] == true 也行,used[i - 1] == false 也行
//关键词:可包含重复数字的序列 nums的排列问题
//排列:一定用到used数组。而且每一层都从0开始遍历,遍历used数组中未标记的
//可重,要先排序,对于横向展开的选择再去重,和组合思路一样
注:错误代码
分析:不知道为什么错,难道是used数组不应该设置成全局的?
class Solution { public: //关键词:可包含重复数字的序列 nums的排列问题 //排列:一定用到used数组。而且每一层都从0开始遍历,遍历used数组中未标记的 //可重,要先排序,对于横向展开的选择再去重,和组合思路一样 vector<vector<int>>v; vector<int>mv; vector<bool>used; vector<vector<int>> permuteUnique(vector<int>& nums) { used.resize(nums.size(),false); sort(nums.begin(),nums.end()); backtracing(nums); return v; } void backtracing(vector<int> &nums) { if(mv.size()==nums.size()) { v.push_back(mv); } for(int j=0;j<nums.size();j++) { if(j>0 && nums[j]==nums[j-1] && nums[j-1]==false) { continue; } if(!used[j]) { //cout<<j<<endl; used[j]=true; mv.push_back(nums[j]); backtracing(nums); mv.pop_back(); used[j]=false; } } } };
修改used数组为传进去的参数.还是报错
class Solution { public: //关键词:可包含重复数字的序列 nums的排列问题 //排列:一定用到used数组。而且每一层都从0开始遍历,遍历used数组中未标记的 //可重,要先排序,对于横向展开的选择再去重,和组合思路一样 vector<vector<int>>v; vector<int>mv; vector<vector<int>> permuteUnique(vector<int>& nums) { vector<bool>used; used.resize(nums.size(),false); sort(nums.begin(),nums.end()); backtracing(nums,used); return v; } void backtracing(vector<int> &nums,vector<bool> &used) { if(mv.size()==nums.size()) { v.push_back(mv); } for(int j=0;j<nums.size();j++) { if(j>0 && nums[j]==nums[j-1] && nums[j-1]==false)//这个条件是同层的树层去重 { continue; } if(used[j]==false) { //cout<<j<<endl; used[j]=true; mv.push_back(nums[j]); backtracing(nums,used); mv.pop_back(); used[j]=false; } } } };
修改后正确:
class Solution { public: //关键词:可包含重复数字的序列 nums的排列问题 //排列:一定用到used数组。而且每一层都从0开始遍历,遍历used数组中未标记的 //可重,要先排序,对于横向展开的选择再去重,和组合思路一样 vector<vector<int>>v; vector<int>mv; vector<vector<int>> permuteUnique(vector<int>& nums) { v.clear(); mv.clear(); vector<bool>used; used.resize(nums.size(),false); sort(nums.begin(),nums.end());//先排序再去重 backtracing(nums,used); return v; } void backtracing(vector<int> &nums,vector<bool> &used) { if(mv.size()==nums.size()) { v.push_back(mv); } for(int j=0;j<nums.size();j++) { if (j > 0 && nums[j] == nums[j - 1] && used[j- 1] == false) { continue; } if(used[j]==false) { used[j]=true; mv.push_back(nums[j]); backtracing(nums,used); mv.pop_back(); used[j]=false; } } } };