代码随想录Day10 回溯算法--1

什么是回溯法 回溯法一般能解决的问题

回溯法也可以叫做回溯搜索法,它是一种搜索的方式。

  • 组合问题:N个数里面按一定规则找出k个数的集合
  • 切割问题:一个字符串按一定规则有几种切割方式
  • 子集问题:一个N个数的集合里有多少符合条件的子集
  • 排列问题:N个数按一定规则全排列,有几种排列方式
  • 棋盘问题:N皇后,解数独等等

回溯算法模板 

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

77 组合 

 组合问题 看作树的结构

按照回溯算法的模板 递归函数的参数我们可以边写边确定 函数的终止条件就是path(经过的路径大小)为k的时候return空 然后for循环里面写每一层的逻辑 用index表示每一树层 先放入path里 然后进入递归后 回溯就要弹出path里面的元素

可以对这一题进行优化操作 也就是剪枝 当列表里面所剩余的元素不够我们使用的时候 我们就可以直接跳出循环

class Solution {
public:
    vector<int> path;
    vector<vector<int>> ans;
    void backtracking(int n,int k,int startindex){
        if(path.size()==k){
            ans.push_back(path);
            return;
        }
        for(int i=startindex;i<=n - (k - path.size()) + 1;i++){
            path.push_back(i);
            backtracking(n,k,i+1);
            path.pop_back();
        }
        return;
    }
    vector<vector<int>> combine(int n, int k) {
        backtracking(n,k,1);
        return ans;
    }
};

 17 电话号码的字母组合

本题思路和77组合是一样 只是多了一点技巧 可以用一个数字来让数字对应里面的字母

class Solution {
public:
    const string letterMap[10] = {
        "", 
        "", 
        "abc", 
        "def", 
        "ghi", 
        "jkl", 
        "mno", 
        "pqrs", 
        "tuv", 
        "wxyz",
    };

    vector<string> ans;
    string path;
    void backtracking(int index,const string& digits){
        if(path.size()==digits.size()){
            ans.push_back(path);
            return;
        }
        int digit = digits[index]-'0';
        string letter = letterMap[digit];
        for(int i=0;i<letter.size();i++){
            path.push_back(letter[i]);
            backtracking(index+1,digits);
            path.pop_back();
        }
    }
    vector<string> letterCombinations(string digits) {
        if (digits.size() == 0) {
            return ans;
        }
        backtracking(0,digits);
        return ans;
    }
};

 40 组合总和Ⅱ

 思路同样和77组合一样 但是本题涉及到去重的逻辑 对同一树层进行去重(要提前给数组排序 让相同的数字可以在相邻的地方)去重可以用used数组也可以用set

class Solution {
public:
    vector<int> path;
    vector<vector<int>> ans;
    void backtracking(int index,int sum,vector<int>& candidates,int target){
        if(sum>target){
            return;
        }
        if(sum==target){
            ans.push_back(path);
            return;
        }
        for(int i=index;i<candidates.size();i++){
            if(i>index && candidates[i]==candidates[i-1]){
                continue;
            }
            path.push_back(candidates[i]);
            sum+=candidates[i];
            backtracking(i+1,sum,candidates,target);
            sum-=candidates[i];
            path.pop_back();
        }
        return;
    }                
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(),candidates.end());
        backtracking(0,0,candidates,target);
        return ans;
    }
};

131 分割回文串

回溯算法 首先先写一个判断是否是回文串的函数 然后判断一个区间如果是回文串 用一个string来截取这个区间 放进结果集

93 复原IP地址

这题和131都是一样的思路 就是小细节不一样 这一题首先需要一个函数判断IP整数合不合法 然后每一层的递归逻辑 直接在原来的字符串上做修改 如果整数合法 就在后面加一个'.' 递归的时候index要往后移两位 回溯的时候要删去这个'.'

78 子集

 不是在叶子节点处理结果了 是每一个节点都要处理结果

491 递增子序列

这题同样需要去重 但是不能改变顺序 不能对原来数组进行排序 所以可以用set来去重 用set来装每一个获取过的元素 然后当不满足递增序列的数或者set里面出现过这个数 则continue跳过循环(在树枝里面去重)

class Solution {
public:
    vector<int> path;
    vector<vector<int>> ans;
    void backtracking(vector<int>& nums,int index){
        if(path.size()>1){
            ans.push_back(path);
        }
        unordered_set<int> set;
        for(int i=index;i<nums.size();i++){
            if(!path.empty() && nums[i]<path.back() || set.find(nums[i]) != set.end()){
                continue;
            }
            path.push_back(nums[i]);
            set.insert(nums[i]);
            backtracking(nums,i+1);
            path.pop_back();
        }
        return;
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        backtracking(nums,0);
        return ans;
    }
};

46 全排列 

排列和组合不一样 排列不要求顺序 所以在回溯模板里面for循环不用index来确定每一层的位置 只需要从头往后 然后用used数组来判断 哪一个数字已经被用过了 跳过就可以了 终止条件就是 path的大小和数组的大小相等

class Solution {
public:
    vector<int> path;
    vector<vector<int>> ans;
    void backtracking(vector<int>& nums,vector<bool>& used){
        if(path.size()==nums.size()){
            ans.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();i++){
            if(used[i]==false){
                path.push_back(nums[i]);
                used[i]=true;
                backtracking(nums,used);
                path.pop_back();
                used[i]=false;
            }
        }
        return;
    }
    vector<vector<int>> permute(vector<int>& nums) {
        vector<bool> used(nums.size(),false);
        backtracking(nums,used);
        return ans;
    }
};

47 全排列Ⅱ

在上一题的基础上 加上去重操作 因为排列不要求顺序 所以数组里面相同的数字 会产生重复的结果也就是在同一个树层里面去重和组合总和Ⅱ一个思路 用used数组就可以对一个树层去重

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值