当前位置:   article > 正文

DFS:深搜+回溯+剪枝解决排列、子集问题

DFS:深搜+回溯+剪枝解决排列、子集问题

                                    创作不易,感谢三连支持!! 

一、全排列I

. - 力扣(LeetCode)

  1. class Solution {
  2. public:
  3. //全局变量
  4. vector<vector<int>> ret;
  5. vector<int> path;
  6. bool check[6];
  7. vector<vector<int>> permute(vector<int>& nums)
  8. {
  9. dfs(nums);
  10. return ret;
  11. }
  12. void dfs(vector<int>& nums)
  13. {
  14. if(nums.size()==path.size()) {ret.push_back(path); return;}
  15. for(int i=0;i<nums.size();++i)
  16. {
  17. if(check[i]==false) //说明没选过
  18. {
  19. path.push_back(nums[i]);
  20. check[i]=true;//减枝
  21. dfs(nums);//继续去下一个找
  22. //回溯
  23. path.pop_back();
  24. check[i]=false;
  25. }
  26. }
  27. }
  28. };

二、全排列II

. - 力扣(LeetCode)

 方案1:不合法就continue

  1. class Solution {
  2. public:
  3. vector<vector<int>> ret;
  4. vector<int> path;
  5. bool check[8];
  6. vector<vector<int>> permuteUnique(vector<int>& nums)
  7. {
  8. sort(nums.begin(),nums.end());
  9. dfs(nums);
  10. return ret;
  11. }
  12. void dfs(vector<int>& nums)
  13. {
  14. if(nums.size()==path.size()) {ret.push_back(path);return;}
  15. //思路1:考虑不合法的选择 continue 思路2:考虑合法的才进dfs
  16. for(int i=0;i<nums.size();++i)
  17. {
  18. if(check[i]==true||(i!=0&&nums[i]==nums[i-1]&&check[i-1]==false)) continue;
  19. path.push_back(nums[i]);
  20. check[i]=true;
  21. dfs(nums);//去下一层找
  22. path.pop_back();
  23. check[i]=false;
  24. }
  25. }
  26. };

方案2:合法才能进循环

  1. class Solution {
  2. public:
  3. vector<vector<int>> ret;
  4. vector<int> path;
  5. bool check[8];
  6. vector<vector<int>> permuteUnique(vector<int>& nums)
  7. {
  8. sort(nums.begin(),nums.end());
  9. dfs(nums);
  10. return ret;
  11. }
  12. void dfs(vector<int>& nums)
  13. {
  14. if(nums.size()==path.size()) {ret.push_back(path);return;}
  15. //思路1:考虑不合法的选择 continue 思路2:考虑合法的才进dfs
  16. for(int i=0;i<nums.size();++i)
  17. {
  18. if(check[i]==false&&(i==0||nums[i]!=nums[i-1]||check[i-1]==true))
  19. {
  20. path.push_back(nums[i]);
  21. check[i]=true;
  22. dfs(nums);//去下一层找
  23. path.pop_back();
  24. check[i]=false;
  25. }
  26. }
  27. }
  28. };

三、优美的排列

. - 力扣(LeetCode)

  1. class Solution {
  2. public:
  3. //类似全排列,可以交换位置但是不能重复
  4. int ret=0;
  5. bool check[16];
  6. int countArrangement(int n)
  7. {
  8. dfs(1,n);
  9. return ret;
  10. }
  11. void dfs(int pos,int n)
  12. {
  13. if(pos==n+1) {++ret;return;}
  14. for(int i=1;i<=n;++i)
  15. {
  16. if(check[i]==false&&(i%pos==0||pos%i==0))
  17. {
  18. check[i]=true;
  19. dfs(pos+1,n);
  20. check[i]=false;
  21. }
  22. }
  23. }
  24. };

四、子集I

. - 力扣(LeetCode)

 策略1:决策树以选不选作为参考,结果为叶子节点

  1. class Solution {
  2. public:
  3. //设置全局变量
  4. vector<vector<int>> ret;
  5. vector<int> path;//记录路径
  6. public:
  7. vector<vector<int>> subsets(vector<int>& nums)
  8. {
  9. dfs(nums,0);
  10. return ret;
  11. }
  12. void dfs(vector<int>& nums,int pos)
  13. {
  14. if(pos==nums.size()) { ret.push_back(path); return;}
  15. //选
  16. path.push_back(nums[pos]);
  17. dfs(nums,pos+1);
  18. path.pop_back();//回溯
  19. //不选
  20. dfs(nums,pos+1);
  21. }
  22. };

策略2:决策树以选几个为参考,结果为全部节点 

  1. class Solution {
  2. public:
  3. //设置全局变量
  4. vector<vector<int>> ret;
  5. vector<int> path;
  6. public:
  7. vector<vector<int>> subsets(vector<int>& nums)
  8. {
  9. dfs(nums,0);
  10. return ret;
  11. }
  12. void dfs(vector<int>& nums,int pos)
  13. {
  14. ret.push_back(path);//每一个决策都是结果
  15. for(int i=pos;i<nums.size();++i)
  16. {
  17. path.push_back(nums[i]);
  18. dfs(nums,i+1);
  19. path.pop_back();
  20. }
  21. }
  22. };

五、子集II

. - 力扣(LeetCode)

 

  1. class Solution {
  2. public:
  3. vector<vector<int>> ret;
  4. vector<int> path;
  5. vector<vector<int>> subsetsWithDup(vector<int>& nums)
  6. {
  7. sort(nums.begin(), nums.end());
  8. dfs(nums,0);
  9. return ret;
  10. }
  11. void dfs(vector<int>& nums,int pos)
  12. {
  13. ret.push_back(path);
  14. for(int i=pos;i<nums.size();++i)
  15. {
  16. if(i>pos&&nums[i]==nums[i-1]) continue;
  17. path.push_back(nums[i]);
  18. dfs(nums,i+1);
  19. path.pop_back();
  20. }
  21. }
  22. };

六、找出所有子集的异或总和再求和

. - 力扣(LeetCode)

  1. class Solution {
  2. int sum=0;//记录总和
  3. int path=0;//记录路径
  4. public:
  5. int subsetXORSum(vector<int>& nums)
  6. {
  7. dfs(nums,0);
  8. return sum;
  9. }
  10. void dfs(vector<int>& nums,int pos)
  11. {
  12. sum+=path;
  13. for(int i=pos;i<nums.size();++i)
  14. {
  15. path^=nums[i];
  16. dfs(nums,i+1);
  17. path^=nums[i];//利用消消乐的性质恢复现场
  18. }
  19. }
  20. };

七、字母大小写全排列

. - 力扣(LeetCode)

  1. class Solution {
  2. public:
  3. vector<string> ret; //找返回值
  4. vector<string> letterCasePermutation(string s)
  5. {
  6. dfs(s,0);
  7. return ret;
  8. }
  9. void dfs(string s,int pos)//用传值s 可以直接在原来的s上进行修改
  10. {
  11. while(pos<s.size()&&isdigit(s[pos])) ++pos;
  12. if(pos==s.size()) {ret.push_back(s); return;}
  13. //变
  14. s[pos]^=32; //^=32(空格)可以完成大小写转化!!
  15. dfs(s,pos+1);
  16. s[pos]^=32;
  17. //不变
  18. dfs(s,pos+1);
  19. }
  20. };

八、下一个排列

. - 力扣(LeetCode)

  1. class Solution {
  2. public:
  3. void nextPermutation(vector<int>& nums)
  4. {
  5. if(nums.size()==1) return;//如果只有一个数,就没有必要去修改了
  6. //思路,找尽可能靠右的低位,与一个尽可能小的大数交换 然后再升序后面的剩余元素
  7. for(int i=nums.size()-2;i>=0;--i)
  8. {
  9. if(nums[i]<nums[i+1])
  10. {
  11. for(int j=nums.size()-1;j>i;--j)
  12. {
  13. if(nums[i]<nums[j]) //找到第一个比i大,
  14. {
  15. swap(nums[i],nums[j]);
  16. sort(nums.begin()+i+1,nums.end());//i位置后面的数升序
  17. return;//此时返回结果
  18. }
  19. }
  20. }
  21. }
  22. //如果循环结束都没有找到第一个升序的,说明是全逆序,此时的结果应该是把你直接变成升序
  23. sort(nums.begin(),nums.end());
  24. }
  25. };

九、排列序列

. - 力扣(LeetCode)​​​​​​

  1. class Solution {
  2. public:
  3. string getPermutation(int n, int k)
  4. {
  5. vector<int> factorial(n);//用来统计各个阶乘
  6. factorial[0]=1;
  7. for(int i=1;i<n;++i)//统计1——(n-1)!的阶乘
  8. {
  9. factorial[i]= factorial[i-1]*i;
  10. }
  11. --k;//康托展开
  12. vector<int> check(n+1,1);//可选数
  13. string ret;
  14. ret.reserve(n);
  15. for(int i=1;i<=n;++i)
  16. {
  17. int order=k/factorial[n-i]+1;//确定了康拖的首位
  18. for(int j=1;j<=n;++j)//告诉check数组,该位置得是0 不能再选
  19. {
  20. order-=check[j];
  21. if(order==0)
  22. {
  23. ret.push_back(j+'0');
  24. check[j]=0;//说明此数被选过
  25. break;
  26. }
  27. }
  28. k%=factorial[n-i];//去找下一个数
  29. }
  30. return ret;
  31. }
  32. };

     排列和子集问题就总结到这啦!!回溯有关的题关键就是画树状图,然后根据树状图去思考怎么进行深搜、回溯和剪枝!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/373023
推荐阅读
相关标签
  

闽ICP备14008679号