DFS:深搜+回溯+剪枝解决排列、子集问题
发布时间:2025-06-24 17:07:51 作者:北方职教升学中心 阅读量:940
创作不易,感谢三连支持!!
一、全排列I
. - 力扣(LeetCode)
class Solution {public: //全局变量 vector<vector<int>> ret; vector<int> path; bool check[6]; vector<vector<int>> permute(vector<int>& nums) { dfs(nums); return ret; } void dfs(vector<int>& nums) { if(nums.size()==path.size()) {ret.push_back(path); return;} for(int i=0;i<nums.size();++i) { if(check[i]==false) //说明没选过 { path.push_back(nums[i]); check[i]=true;//减枝 dfs(nums);//继续去下一个找 //回溯 path.pop_back(); check[i]=false; } } }};
二、全排列II
. - 力扣(LeetCode)
方案1:不合法就continue
class Solution {public: vector<vector<int>> ret; vector<int> path; bool check[8]; vector<vector<int>> permuteUnique(vector<int>& nums) { sort(nums.begin(),nums.end()); dfs(nums); return ret; } void dfs(vector<int>& nums) { if(nums.size()==path.size()) {ret.push_back(path);return;} //思路1:考虑不合法的选择 continue 思路2:考虑合法的才进dfs for(int i=0;i<nums.size();++i) { if(check[i]==true||(i!=0&&nums[i]==nums[i-1]&&check[i-1]==false)) continue; path.push_back(nums[i]); check[i]=true; dfs(nums);//去下一层找 path.pop_back(); check[i]=false; } }};
方案2:合法才能进循环
class Solution {public: vector<vector<int>> ret; vector<int> path; bool check[8]; vector<vector<int>> permuteUnique(vector<int>& nums) { sort(nums.begin(),nums.end()); dfs(nums); return ret; } void dfs(vector<int>& nums) { if(nums.size()==path.size()) {ret.push_back(path);return;} //思路1:考虑不合法的选择 continue 思路2:考虑合法的才进dfs for(int i=0;i<nums.size();++i) { if(check[i]==false&&(i==0||nums[i]!=nums[i-1]||check[i-1]==true)) { path.push_back(nums[i]); check[i]=true; dfs(nums);//去下一层找 path.pop_back(); check[i]=false; } } }};
三、优美的排列
. - 力扣(LeetCode)
class Solution {public: //类似全排列,可以交换位置但是不能重复 int ret=0; bool check[16]; int countArrangement(int n) { dfs(1,n); return ret; } void dfs(int pos,int n) { if(pos==n+1) {++ret;return;} for(int i=1;i<=n;++i) { if(check[i]==false&&(i%pos==0||pos%i==0)) { check[i]=true; dfs(pos+1,n); check[i]=false; } } }};
四、子集I
. - 力扣(LeetCode)
策略1:决策树以选不选作为参考,结果为叶子节点
class Solution {public: //设置全局变量 vector<vector<int>> ret; vector<int> path;//记录路径public: vector<vector<int>> subsets(vector<int>& nums) { dfs(nums,0); return ret; } void dfs(vector<int>& nums,int pos) { if(pos==nums.size()) { ret.push_back(path); return;} //选 path.push_back(nums[pos]); dfs(nums,pos+1); path.pop_back();//回溯 //不选 dfs(nums,pos+1); }};
策略2:决策树以选几个为参考,结果为全部节点
class Solution {public: //设置全局变量 vector<vector<int>> ret; vector<int> path;public: vector<vector<int>> subsets(vector<int>& nums) { dfs(nums,0); return ret; } void dfs(vector<int>& nums,int pos) { ret.push_back(path);//每一个决策都是结果 for(int i=pos;i<nums.size();++i) { path.push_back(nums[i]); dfs(nums,i+1); path.pop_back(); } }};
五、子集II
. - 力扣(LeetCode)
class Solution {public: vector<vector<int>> ret; vector<int> path; vector<vector<int>> subsetsWithDup(vector<int>& nums) { sort(nums.begin(), nums.end()); dfs(nums,0); return ret; } void dfs(vector<int>& nums,int pos) { ret.push_back(path); for(int i=pos;i<nums.size();++i) { if(i>pos&&nums[i]==nums[i-1]) continue; path.push_back(nums[i]); dfs(nums,i+1); path.pop_back(); } }};
六、找出所有子集的异或总和再求和
. - 力扣(LeetCode)
class Solution { int sum=0;//记录总和 int path=0;//记录路径public: int subsetXORSum(vector<int>& nums) { dfs(nums,0); return sum; } void dfs(vector<int>& nums,int pos) { sum+=path; for(int i=pos;i<nums.size();++i) { path^=nums[i]; dfs(nums,i+1); path^=nums[i];//利用消消乐的性质恢复现场 } }};
七、字母大小写全排列
. - 力扣(LeetCode)
class Solution {public: vector<string> ret; //找返回值 vector<string> letterCasePermutation(string s) { dfs(s,0); return ret; } void dfs(string s,int pos)//用传值s 可以直接在原来的s上进行修改 { while(pos<s.size()&&isdigit(s[pos])) ++pos; if(pos==s.size()) {ret.push_back(s); return;} //变 s[pos]^=32; //^=32(空格)可以完成大小写转化!! dfs(s,pos+1); s[pos]^=32; //不变 dfs(s,pos+1); }};
八、下一个排列
. - 力扣(LeetCode)
class Solution {public: void nextPermutation(vector<int>& nums) { if(nums.size()==1) return;//如果只有一个数,就没有必要去修改了 //思路,找尽可能靠右的低位,与一个尽可能小的大数交换 然后再升序后面的剩余元素 for(int i=nums.size()-2;i>=0;--i) { if(nums[i]<nums[i+1]) { for(int j=nums.size()-1;j>i;--j) { if(nums[i]<nums[j]) //找到第一个比i大, { swap(nums[i],nums[j]); sort(nums.begin()+i+1,nums.end());//i位置后面的数升序 return;//此时返回结果 } } } } //如果循环结束都没有找到第一个升序的,说明是全逆序,此时的结果应该是把你直接变成升序 sort(nums.begin(),nums.end()); }};
九、排列序列
. - 力扣(LeetCode)
class Solution {public: string getPermutation(int n, int k) { vector<int> factorial(n);//用来统计各个阶乘 factorial[0]=1; for(int i=1;i<n;++i)//统计1——(n-1)!的阶乘 { factorial[i]= factorial[i-1]*i; } --k;//康托展开 vector<int> check(n+1,1);//可选数 string ret; ret.reserve(n); for(int i=1;i<=n;++i) { int order=k/factorial[n-i]+1;//确定了康拖的首位 for(int j=1;j<=n;++j)//告诉check数组,该位置得是0 不能再选 { order-=check[j]; if(order==0) { ret.push_back(j+'0'); check[j]=0;//说明此数被选过 break; } } k%=factorial[n-i];//去找下一个数 } return ret; }};
排列和子集问题就总结到这啦!!回溯有关的题关键就是画树状图,然后根据树状图去思考怎么进行深搜、回溯和剪枝!!