1.回溯总结 主要参考下面的题目,代码随想 ,还有labuladong ,这个类型的主要思路就是,找
到结束条件,然后就是进行选择,放入之后,就再次进行放出
1 2 3 4 5 6 7 8 9 10 11 result = [] def backtrack(路径, 选择列表): if 满足结束条件: result.add(路径) return for 选择 in 选择列表: 做选择 backtrack(路径, 选择列表) 撤销选择
包含下面几个题目
46全排列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution {public : vector<vector<int >> ans; vector<int > res; vector<bool > st; vector<vector<int >> permute (vector<int >& nums) { st=vector <bool > (nums.size ()); dfs (nums,0 ); return ans; } void dfs (vector<int > &nums,int u) { if (u==nums.size ()){ ans.push_back (res); return ; } for (int i = 0 ; i < nums.size (); ++i) { if (!st[i]){ st[i]=true ; res.push_back (nums[i]); dfs (nums,u+1 ); res.pop_back (); st[i]=false ; } } } };
首先还是使用ans进行保存,使用st进行验证,u代表成功的个数,当为n,就是可以进行添加,从0开始进行访问所有的值
51.N皇后 这个题目就是进行求解,他的check条件就是列上没有,,斜线上没有,斜线就是当前的点(x-1,y-1),(x-1,y+1),检查这些值有没有,没有就放,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Solution {public : vector<vector<string>> ans; vector<string> res; vector<vector<string>> solveNQueens (int n) { res=vector <string>(n,string (n,'.' )); dfs (n,0 ); return ans; } void dfs (int n,int u) { if (n==u){ ans.push_back (res); return ; } for (int i = 0 ; i < n; ++i) { if (isok (u,i)){ res[u][i]='Q' ; dfs (n,u+1 ); res[u][i]='.' ; } } } bool isok (int u,int i) { int n=res.size (); for (int j = 0 ; j <=u; ++j) { if (res[j][i]=='Q' )return false ; } for (int j = u-1 ,k=i-1 ; j>=0 &&k>=0 ; j--,k--) { if (res[j][k]=='Q' )return false ; } for (int j = u-1 ,k=i+1 ; j>=0 &&k<n ; j--,k++) { if (res[j][k]=='Q' )return false ; } return true ; } };
112路径总和 从根节点开始寻找,两种dfs方式,一种就是在for循环里面进行回溯,还有一种在最开始进行回溯。
这个就是在里面进行回溯,因为是直接开始减的,所以,不需要把target还原
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution {public : bool hasPathSum (TreeNode* root, int targetSum) { if (!root)return false ; return dfs (root,targetSum-root->val); } bool dfs (TreeNode* root,int target) { if (target==0 &&!root->left&&!root->right){ return true ; } if (root->left){ bool left= dfs (root->left,target-root->left->val); if (left)return true ; } if (root->right){ if (dfs (root->right,target-root->right->val)){ return true ; } } return false ; } };
140.单词拆分 这个题目还是回溯,本质就是对i从u开始,进行截取,然后,看是不是在指点里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Solution {public : unordered_map<string,int > rec; vector<string> ans; string path; vector<string> wordBreak (string s, vector<string>& wordDict) { for (auto word:wordDict){ rec[word]++; } dfs (s,0 ); return ans; } void dfs (string s,int u) { if (u==s.size ()){ path.pop_back (); ans.push_back (path); path+=" " ; return ; } if (u>s.size ()){ return ; } for (int i = u; i <s.size () ; ++i) { auto t=s.substr (u,i-u+1 ); if (rec.find (t)!=rec.end ()){ auto x=path; path+=t+" " ; dfs (s,i+1 ); path=x; } } } };
17.电话组合 还不是组合一样,放入字符串里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution {public : vector<string> ans; string strs[10 ] = { "" , "" , "abc" , "def" , "ghi" , "jkl" , "mno" , "pqrs" , "tuv" , "wxyz" , }; string path; vector<string> letterCombinations (string digits) { if (digits.empty ())return ans; dfs (digits,0 ); return ans; } void dfs (string digits,int u) { if (u==digits.size ()){ ans.push_back (path); return ; } if (u>digits.size ())return ; int n=digits[u]-'0' ; for (int i = 0 ; i < strs[n].size (); ++i) { path.push_back (strs[n][i]); dfs (digits,u+1 ); path.pop_back (); } } };
22.括号生成 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Solution {public : vector<string> ans; string path; unordered_map<char ,int > rec; vector<string> generateParenthesis (int n) { dfs (n,0 ); return ans; } void dfs (int n,int u) { if (u==2 *n){ ans.push_back (path); return ; } if (rec.find ('(' )==rec.end ()||rec['(' ]<n){ path+='(' ; rec['(' ]++; dfs (n,u+1 ); path.pop_back (); rec['(' ]--; } if (rec.find (')' )==rec.end ()||rec[')' ]<rec['(' ]){ path+=')' ; rec[')' ]++; dfs (n,u+1 ); path.pop_back (); rec[')' ]--; } } };
两个剪纸,一个是长度都是n,括号,还有一个就是右括号小于左的
39.组合总和 这个就是组合数,使用i到u进行遍历,然后还有一个就是,直接从i开始,不是i+1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution {public : vector<vector<int >> ans; vector<int > path; vector<vector<int >> combinationSum (vector<int >& candidates, int target) { sort (candidates.begin (),candidates.end ()); dfs (candidates,target,0 ); return ans; } void dfs (vector<int > &nums,int u,int index) { if (u==0 ){ ans.push_back (path); return ; } if (index==nums.size ())return ; if (u<0 )return ; for (int i = index; i < nums.size (); ++i) { path.push_back (nums[i]); dfs (nums,u-nums[i],i); path.pop_back (); } } };
这个就是从i即系开始,可以重复使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Solution { public: vector<vector<int>> ans; vector<int> path; vector<vector<int>> combinationSum(vector<int>& c, int target) { dfs(c,0,target); return ans; } void dfs(vector<int>& c,int u,int target ){ // u代表当前循环到哪一个了数字呢 // 考虑边界 if(target==0){ ans.push_back(path); return; } if(u==c.size()){ return; // 直接返回 } // 接下来就是放入放出 for(int i=0;i*c[u]<=target;i++){ dfs(c,u+1,target-i*c[u]); path.push_back(c[u]); // 这是选择加入一个后,再对后面的进行加入 // target-i*c[u]是加入当前的次数,u+1是下一个值 // 回复现场 } for(int i=0;i*c[u]<=target;i++){ path.pop_back(); } } };
这个代码很不错,因为是先dfs然后再push,为什么了,因为先dfs,代表选0个当前的,之后放入一个,就是选了一个dfs,而且是在dfs之后选择下一个值,而且,target因为是没有变,所以直接减去i*cu
698.划分k个相等的子集 首先的先决条件就是sum数组的和可以整除k,而且最后一个数,一定小于平均数。之后就是进行回溯,代码思路,使用dfs(nums,avg,k,u)
含义就是当avg为0的时候,可以进行k-1(表示又有一组满足了,然后u从开始进行寻找,avg再次恢复到初始值)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class Solution { public: vector<vector<int>> ans; vector<int>path; vector<bool> st; int t; int avg; bool canPartitionKSubsets(vector<int>& nums, int k) { auto sum=accumulate(nums.begin(),nums.end(),0); if(sum%k!=0)return false; st=vector<bool>(nums.size(),false); int target=sum/k; if (nums.back()>target)return false; // t=k; avg=target; sort(nums.begin(),nums.end()); return dfs(nums,target,0,0); } bool dfs(vector<int> &nums,int target,int index,int u){ if(u==t)return true; // cout<<u; if(target==0)return dfs(nums,avg,0,u+1); // if(target<0)return false; for (int i = index; i < nums.size(); ++i) { if(!st[i]&&target-nums[i]>=0){ st[i]=true; // path.push_back(nums[i]); if(dfs(nums,target-nums[i],i+1,u)){ return true; } st[i]=false; // path.pop_back(); //排好序了的,如果当前都没办法减少到0,后面就不可能 if (target==avg)return false; } } return false; } };
if (target==avg)return false;重要剪纸,当表示值不变还是avg,就表示后面更不可能进行减少值,因为后面的都比当前值大,target会一直不变,所以不能选择,直接失败
77.组合数 组合数,就是进行选择,首先进行排序,i到u开始,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution {public : vector<vector<int >> ans; vector<int > rec; vector<bool >st; vector<vector<int >> combine (int n, int k) { st=vector <bool > (n+1 ,false ); dfs (n,k,1 ); return ans; } void dfs (int n,int k,int u) { if (!k){ ans.push_back (rec); return ; } for (int i = u; i < n+1 ; ++i) { rec.push_back (i); dfs (n,k-1 ,i+1 ); rec.pop_back (); } } };
78,幂集 就是返回所有的选择,直接在上面的进行手动设置k
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution {public : vector<vector<int >> ans; vector<int > rec; vector<vector<int >> subsets (vector<int >& nums) { sort (nums.begin (),nums.end ()); for (int i = 0 ; i <=nums.size (); ++i) { dfs (nums,i,0 ); } return ans; } void dfs (vector<int > &nums,int k,int u) { if (!k){ ans.push_back (rec); return ; } for (int i = u; i < nums.size (); ++i) { rec.push_back (nums[i]); dfs (nums,k-1 ,i+1 ); rec.pop_back (); } } };
40.组合综合 进行筛选,而且不能含有重复的,首先进行选择就是i到u,去重就是要使用那个continue,与前面相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution {public : vector<int > path; vector<vector<int >> ans; vector<vector<int >> combinationSum2 (vector<int >& candidates, int target) { sort (candidates.begin (),candidates.end ()); dfs (candidates,target,0 ); return ans; } void dfs (vector<int > &nums,int target,int u) { if (target==0 ){ ans.push_back (path); return ; } if (u>=nums.size ())return ; for (int i = u; i < nums.size (); ++i) { if (nums[i]>target)return ; if (i>u&&nums[i]==nums[i-1 ])continue ; path.push_back (nums[i]); dfs (nums,target-nums[i],i+1 ); path.pop_back (); } } };
if (i>u&&nums[i]==nums[i-1])continue;去重代码,不需要使用st,来表示,因为后面都是没有访问的
216.组合综合 还是之前一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution {public :vector<int > path; vector<vector<int >> ans; vector<vector<int >> combinationSum3 (int k, int n) { dfs (1 ,k,n); return ans; } void dfs (int start,int k,int n) { if (!k){ if (!n)ans.push_back (path); }else if (k){ for (int i=start;i<=9 ;i++){ if (n>=i){ path.push_back (i); dfs (i+1 ,k-1 ,n-i); path.pop_back (); } } } } };
131.分割回文串 还是之前一样,check条件就是是不是会问,如果是true,直接返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Solution {public : vector<vector<string>> ans; vector<string> path; vector<vector<string>> partition (string s) { dfs (s,0 ); return ans; } void dfs (string s,int u) { if (u==s.size ()) { ans.push_back (path); return ; } for (int i = u; i < s.size (); ++i) { auto x=s.substr (u,i-u+1 ); if (is_ok (x)){ path.push_back (x); dfs (s,i+1 ); path.pop_back (); } } } bool is_ok (string s) { auto x=s; reverse (s.begin (),s.end ()); return x==s; } };
93复原ip地址 这个首先是对于0,直接继续,而且,一共只有4个小数点,最后一个在加入的时候去除,之后就是普通的回溯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class Solution {public : int start; int end; vector<string> ans; string path; vector<string> restoreIpAddresses (string s) { start=0 ,end=255 ; dfs (s,4 ,0 ); return ans; } void dfs (string s,int times,int u) { if (!times&&u==s.size ()){ path.pop_back (); ans.push_back (path); return ; } if (times<0 )return ; if (u>=s.size ())return ; if (s[u]=='0' ){ path+="0." ; dfs (s,times-1 ,u+1 ); return ; }else { for (int i = u; i < s.size (); ++i) { if (i-u+1 >3 )return ; auto x=s.substr (u,i-u+1 ); auto t= stoi (x); if (t<=255 ){ auto back=path; path+=x+"." ; dfs (s,times-1 ,i+1 ); path=back; } } } } };
对于签到0的优化方法就是把循环的下一位变成u+1,不是s.size
1 2 3 4 int n=u+1 ; if (s[u]!='0' )n=s.size (); for (int i = u; i < n; ++i) {
90子集 这个就是进行去重,直接看是不是相等,然后continue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution {public : vector<vector<int >> ans; vector<int > path; vector<vector<int >> subsetsWithDup (vector<int >& nums) { sort (nums.begin (),nums.end ()); for (int i = 0 ; i <=nums.size (); ++i) { dfs (nums,i,0 ); } return ans; } void dfs (vector<int > &nums,int n,int u) { if (n==0 ){ ans.push_back (path); return ; } if (u>=nums.size ())return ; if (n<0 )return ; for (int i =u; i < nums.size (); ++i) { if (i>u&&nums[i]==nums[i-1 ])continue ; path.push_back (nums[i]); dfs (nums,n-1 ,i+1 ); path.pop_back (); } } };
因为都是排序了的,后面一定没有访问,所以不用st
47全排列 进行去重,这个就是需要st数组,因为从是排列,而且需要进行去重,所以是从0开始的,需要st
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution {public : vector<vector<int >> ans; vector<int > path; vector<bool > st; vector<vector<int >> permuteUnique (vector<int >& nums) { sort (nums.begin (),nums.end ()); path= vector <int >(nums.size ()); st= vector <bool >(nums.size ()); dfs (nums,0 ); return ans; } void dfs (vector<int >& nums,int u ) { if (u==nums.size ()){ ans.push_back (path); return ; } for (int i=0 ;i<nums.size ();i++){ if (!st[i]){ if (i&&nums[i-1 ]==nums[i]&&!st[i-1 ])continue ; st[i] = true ; path[u] = nums[i]; dfs (nums, u + 1 ); st[i] = false ; } } } };
491递增子序列 因为没有排序,不能排序,所以只能使用set来进行作为
本人思路,就是对所有n的n从2到size进行遍历,然后加进去,使用set去除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Solution {public : vector<vector<int >> ans; vector<int > path; set<vector<int >> s; vector<vector<int >> findSubsequences (vector<int >& nums) { for (int i = 2 ; i <=nums.size (); ++i) { dfs (nums,i,0 ); } return ans; } void dfs (vector<int >& nums,int n,int u) { if (n==0 ){ if (s.find (path)==s.end ()){ ans.push_back (path); s.insert (path); } return ; } if (u==nums.size ())return ; for (int i = u; i <nums.size () ; ++i) { if (path.empty ()||nums[i]>=path.back ()){ path.push_back (nums[i]); dfs (nums,n-1 ,i+1 ); path.pop_back (); } } } };
2.小结 上面的几个经典题目,一个去重,前面相等直接跳过
1 if(i>u&&num[i]==num[i-1])continue
第二个就是前导0的问题,一般是只能用做单独的0,那么距离就是u+1
1 2 3 int n=s.size(); if(s[i]=='0')n=u+1: for(int i=u;i<n;i++)
第三个就是排列组合,排列的话就是从0开始,设置st数组.
组合的就是从u开始,去重和上面是一样的