ccpocker ccpocker‘s blog

BackTracking Review

2021-05-01
ccpocker

综述

BackTracking 称之为回溯法。回溯法本质上是一种穷举,穷举所有可能,然后找到我们所需要的答案。

回溯法解决的问题:

  • 组合问题:
  • 切割问题:
  • 子集问题:
  • 排列问题:
  • 棋盘问题:

回溯法的模板

每个回溯算法都可以抽象为一个N叉树的搜索,集合的大小构建了树的宽度,递归的深度构成了树的深度。参考图:figure1

figure1

void backTracking(参数){
    //终止条件
    if(终止条件){
        存放结果;
        return;
    }
    //回溯搜索的遍历过程
    for(选择:本层集合中的元素(树中结点孩子的数量就是集合的大小)){//横向遍历
        处理节点;
        backTracking(路径,选择列表); //递归,纵向遍历
        回溯,撤销处理结果
    }
}

组合问题

LeetCode 77 组合

组合是无序的,例如[1,2]和[2,1]是同一个组合;

//leetcode 77. 组合
void backTracking77(vector<vector<int>> &res,vector<int> &cur,int n,int k,int start){
    if(cur.size()==k){
        res.emplace_back(cur);
        return;
    }
    //可剪枝优化,for(int i=start;i<=n-(k-path.size())+1;++i)
    for(int i=start;i<=n;++i){ 
        
        cur.push_back(i);
        backTracking77(res,cur,n,k,i+1);
        cur.pop_back(); //撤销
    }

}
vector<vector<int>> combine(int n, int k) {
    vector<vector<int>> res;
    vector<int> cur;
    backTracking77(res,cur,n,k,1);
    return res;
}

LeetCode 216.组合总和Ⅲ

void backTracking216(vector<vector<int>> &res,vector<int> &cur,int n,int k,int start){
    if(k==0){
        if(n==0) res.emplace_back(cur);
        return;
    }

    for(int i=start;i<=min(9,n);++i){
        cur.push_back(i);
        backTracking216(res,cur,n-i,k-1,start=i+1);
        cur.pop_back();
    }

}

vector<vector<int>> combinationSum3(int k, int n) {
    vector<vector<int>> res;
    vector<int> cur;
    backTracking216(res,cur,n,k,1);
    return res;
}

LeetCode 17.电话号码的字母组合

vector<string> vec_str17{"abc","def","ghi","jkl","mno","pqrs","tuv","xyz"};
void backTracking17(vector<string> &res,string curStr,string digits,int start)
{
    if(start==digits.size()){
        res.emplace_back(curStr);
        return;
    }


    char c=digits[start];
    if (c!='1'&&c!='0'){
        string temp=vec_str17[c-'2'];
        for(char c:temp){
            curStr+=c;
            backTracking17(res,curStr,digits,start+1);
            curStr.pop_back();
        }
    }
    
}

vector<string> letterCombinations(string digits) {
    vector<string> res;
    if(digits.empty()||(digits.size()==0&&digits=="1"))
        return res;
    string curStr;
    backTracking17(res,curStr,digits,0);
    return res;

}

LeetCode 39

//LeetCode 39.组合总和I
void backTracking39(vector<vector<int>> &res,vector<int> &cur,vector<int> &candidates,int n,int start){
    if(n==0){
        res.emplace_back(cur);
        return;
    }
    for(int i=start;i<candidates.size()&&candidates[i]<=n;++i){
        cur.push_back(candidates[i]);
        backTracking39(res,cur,candidates,n-candidates[i],i);
        cur.pop_back();
    }

}

vector<vector<int>> combinationSum(vector<int>& candidates, int target){
    vector<vector<int>> res;
    vector<int> cur;
    sort(candidates.begin(),candidates.end());
    backTracking39(res,cur,candidates,target,0);
    return res;
}

LeetCode 39

//LeetCode 40.组合总和II
/*
去重使用一个used数组,同一树层级使用过的数字不再使用,但同分支的相同数字是可以继续使用的。
*/
void backTracking39(vector<vector<int>> &res,vector<int> &cur,vector<int> &candidates,vector<bool> &used,int n,int start){
    if(n==0){
        res.emplace_back(cur);
        return;
    }
    for(int i=start;i<candidates.size()&&candidates[i]<=n;++i){
        if(i>0&&candidates[i]==candidates[i-1]&&!used[i-1]){
            continue;
        }else{
            cur.push_back(candidates[i]);
            used[i]=true;
            backTracking39(res,cur,candidates,used,n-candidates[i],i+1);
            used[i]=false;
            cur.pop_back();
        }
    }

}

vector<vector<int>> combinationSum2(vector<int>& candidates, int target){
    vector<vector<int>> res;
    vector<bool> used(candidates.size(),false);
    vector<int> cur;
    sort(candidates.begin(),candidates.end());
    backTracking39(res,cur,candidates,used,target,0);
    return res;
}

Comments

Content