LeetCode 回溯算法总结

回溯法四舍五入和深度优先搜索差别多,一次深搜找到一种情况,然后根据递归的结束返回到上一次的递归部分,产生下一种情况。

  1. 全排列
class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
       vector<vector<int>> res;
       vector<int> s;
       vector<int> visited(nums.size(),0);//visited数组标记数组中的元素是否已被存入
       f(res,nums,s,visited);
       return res;
    }
    void f(vector<vector<int>>&res,vector<int>&nums,vector<int>&s,vector<int>&visited){
        if(s.size()==nums.size()){//递归出口
            res.push_back(s);
            return;
        }
        for(int i=0;i<nums.size();i++){//因为判断数组中元素被没被访问需要从头开始,所以不需要start标记起始位置
            if(visited[i]==1)
                continue;
            visited[i]=1;
            s.push_back(nums[i]);
            f(res,nums,s,visited);
            s.pop_back();
            visited[i]=0;//从序列中弹出的同时,也要将visited数组改回去
        }
    }
};
  1. 子集
class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int >> result;
        vector<int> tmp;//创建子集
        f(result,nums,0,tmp);
        return result;
    }
    void f(vector<vector<int>>& result, vector<int> nums, int startPoint, vector<int> tmp){
        result.push_back(tmp);
        for(int i=startPoint;i<nums.size();i++){
            tmp.push_back(nums[i]);
            f(result,nums,i+1,tmp);//数组下标也需要作为参数传入,否则每次从头开始会重复
            tmp.pop_back();//将子集元素弹出
        }
    }
};
  1. 组合总和
class Solution {
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int>> res;
        vector<int> s;
        f(0,res,s,candidates,target);
        return res;
    }
    void f(int start,vector<vector<int>>&res,vector<int> s,vector<int>candidates,int target){
        if(target==0){//找到合适序列,存入数组
            res.push_back(s);
            return;
        }
        if(target<0){//说明这种序列不合理,返回
            return;
        }
        for(int i=start;i<candidates.size();i++){
            s.push_back(candidates[i]);
            f(i,res,s,candidates,target-candidates[i]);
            s.pop_back();
        }
    }
};
  1. 数组总和II
class Solution {
public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<vector<int>> res;
        vector<int> s;
        sort(candidates.begin(),candidates.end());//排序
        f(0,res,s,candidates,target);
        return res;
    }
    void f(int start,vector<vector<int>>&res,vector<int> s,vector<int>candidates,int target){
        if(target==0){
            res.push_back(s);
            return;
        }
        if(target<0){
            return;
        }
        for(int i=start;i<candidates.size();i++){
            if(i>start&&candidates[i]==candidates[i-1])//因为序列是已排序的,所以跳过相同的元素即可使结果不重复
                continue;
            s.push_back(candidates[i]);
            f(i+1,res,s,candidates,target-candidates[i]);//因为一个元素只能使用一次,所以参数传入下一个字符
            s.pop_back();
        }
    }
};
  1. 组合总和
    画树状图就号理解了,儿子节点表示递归,兄弟节点表示循环。
class Solution {
public:
    vector<vector<int>> combinationSum3(int k, int target) {
        vector<vector<int>> res;
        vector<int> s;
        f(0,res,s,k,target);
        return res;
    }
    void f(int start,vector<vector<int>>&res,vector<int>&s,int k,int target){
        if(target==0&&k==0){
            res.push_back(s);
            return;
        }
        if(target<0){
            return;
        }
        for(int i=start+1;i<=9;i++){
            s.push_back(i);
            f(i,res,s,k-1,target-i);
            s.pop_back();
        }
    }
};
  1. 括号生成
    这道题和之前的区别在于,当我们确定之前括号有序时才能继续添加括号,所以我们可以添加记录左右括号的个数
class Solution {
public:
    vector<string> generateParenthesis(int n) {
        vector<string> res;
        f(res,"",n,0,0);
        return res;
    }
    void f(vector<string> &res,string s,int n,int left,int right){
        if(s.length()==2*n){
            res.push_back(s);
            return;
        }
        if(left<n)//左括号个数小于n
            f(res,s+'(',n,left+1,right);
        if(left>right)//右括号个数小于左括号
            f(res,s+')',n,left,right+1);
    }
};
  1. 字母大小写全排列
    这类题本来是对于每个元素都先考虑放它的情况,再考虑不放它的情况,这道题是,对于每个字母,先考虑放它,再考虑放它的另一种大小写形式:如果是数字,就直接加进去,然后下一层递归;如果是字母,就先加进去,然后下一层递归;再加改变大小写形式, 然后下一层递归。
class Solution {
public:
    vector<string> res;
    vector<string> letterCasePermutation(string S) {
        dfs(S,0);
        return res;
    }
    void dfs(string s,int i){
        if(i==s.size()){//递归出口
            res.push_back(s);
            return;
        }
        if(s[i]>='A'&&s[i]<='Z'){
            dfs(s,i+1);
            s[i]+=32;
            dfs(s,i+1);
        }else if(s[i]>='a'&&s[i]<='z'){
            dfs(s,i+1);
            s[i]-=32;
            dfs(s,i+1);
        }else dfs(s,i+1);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值