代码随想录算法训练营第19天 | 第七章 回溯算法part01

代码随想录算法训练营第19天 | 第七章 回溯算法part01

理论基础

其实在讲解二叉树的时候,就给大家介绍过回溯,这次正式开启回溯算法,大家可以先看视频,对回溯算法有一个整体的了解。
回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案。它的基本思想就是通过递归地选择并尝试解决问题的每个部分,当发现当前选择不符合条件时,会回退(撤销选择),然后继续尝试其他选择。回溯法解决的问题都可以抽象为树形结构,是的,我指的是所有回溯法的问题都可以抽象为树形结构!

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

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

for循环就是遍历集合区间,可以理解一个节点有多少个孩子,这个for循环就执行多少次。
backtracking这里自己调用自己,实现递归。
for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这棵树全遍历完了。

77. 组合

对照回溯算法理论基础给出的代码模板,来做本题组合问题,大家就会发现写回溯算法的套路。

在回溯算法解决实际问题的过程中,大家会有各种疑问,先看视频介绍,基本可以解决大家的疑惑。

本题关于剪枝操作是大家要理解的重点,因为后面很多回溯算法解决的题目,都是这个剪枝套路。

在这里插入图片描述
每次从集合中选取元素,可选择的范围随着选择的进行而收缩,调整可选择的范围,图中可以发现n相当于树的宽度,k相当于树的深度。每次搜索到了叶子节点,我们就找到了一个结果。
实际上看代码还算比较好理解,回溯的核心就是用完以后就扔了,push以后就pop,遍历下一个。
至于剪枝优化也很简单,主要是n - (k - path.size()) + 1。没优化前,i是一直要遍历到n。现在我们只要遍历到n - (k - path.size()) + 1即可。k - path.size()即为冗余,假设有n=10个元素,需要k=5个元素,已经取了path.size()=3个元素,这时候还需要去俩,那么前一步最多只能遍历到10-2=8,二现在这时候我们要从9开始取,所以再+1。当把代码思路整理了一下后,就会发现其实难度还可以。只要多思考下,建议先看一遍视频,就能把思路理顺。

class Solution {
public:
vector<vector<int>> result; 
vector<int> path; 
void backtracking(int n, int k, int startIndex){
        if (path.size() == k) {
            result.push_back(path);
            return;
            }//满足条件,return
            for(int i=startIndex;i<=n - (k - path.size()) + 1;i++)
            {
                path.push_back(i);
                backtracking(n, k, i + 1); //继续递归
                path.pop_back();//回溯  
            }
}
    vector<vector<int>> combine(int n, int k) {
        result.clear(); // 可以不写
        path.clear();   // 可以不写
        backtracking(n, k, 1);
        return result;
    }
};

216. 组合总和 III

如果把组合问题理解了,本题就容易一些了。

  • 题目链接/文章讲解
  • 视频讲解
    这题要求只使用数字1到9, 每个数字 最多使用一次,那么这题本质上就和上面一题没有本质上区别.我用了一个gap来判断是不是满足条件,每往数组里加一个数 ,就重新计算下目标值差距. 注意,gap也要回溯, 进行先加后减处理
class Solution {
public:
vector<vector<int>> result; 
vector<int> path; 
int gap=0;
void backtracking(int n, int k, int startIndex){  
        if (path.size() == k&&gap==0) {
            result.push_back(path);
            return;
            }//满足条件,return
        if(gap<0||path.size() == k)
            return;//没找到,不满足条件,也return
        for(int i=startIndex;i<=9;i++)
            {
                path.push_back(i);
                gap-=i;
                backtracking(n, k, i + 1); //继续递归
                path.pop_back();//回溯  
                gap+=i;//sum也要回溯
            }
}
    vector<vector<int>> combinationSum3(int k, int n) {
        result.clear(); // 可以不写
        path.clear();   // 可以不写
        sum=n;
        backtracking(n, k, 1);
        return result;
    }
};

17. 电话号码的字母组合

本题刚开始做会有点难度,建议先自己思考 20 分钟,没思路就直接看题解。
letterMap 是一个常量字符串数组,用于将数字映射到字母。例如,数字 2 对应 “abc”,数字 7 对应 “pqrs”。
终止条件: 如果当前组合的长度 s.size() 达到了输入数字字符串 digits 的长度,表示已经生成了一个完整的组合,将其加入 result 中。

循环部分: 对于当前数字 digits[num],通过 letterMap[digits[num] - ‘0’] 获取对应的字母集,然后逐个将字母添加到组合字符串 s 中,递归调用 backtracking 来继续生成下一个字母,直到找到完整的组合。

回溯: 每次递归完成后,调用 s.pop_back() 来撤销上一步的选择,进入下一个字母的递归。

class Solution {
public:
const string letterMap[10] = {
    " ", // 0
    "", // 1
    "abc", // 2
    "def", // 3
    "ghi", // 4
    "jkl", // 5
    "mno", // 6
    "pqrs", // 7
    "tuv", // 8
    "wxyz", // 9
};
    vector<string> result;
    string s;
    void backtracking(string digits, int num){
        if(s.size()==digits.size()){
            result.push_back(s);
            return;
        }
    for (char ch :letterMap[digits[num]-'0']) {
        s.push_back(ch);
        backtracking(digits,num+1);
        s.pop_back();
    }
}
    vector<string> letterCombinations(string digits) {
        s.clear();
        result.clear();
        if (digits.size() == 0) {
            return result;
        }
        backtracking(digits, 0);
        return result;
    }
};
  • 题目链接/文章讲解
  • 视频讲解
    确实,刚开始学回溯算法,有点头疼,但是当自己逐渐学懂了,其实难度也还行,本质上和递归一样,暴力穷举,不断试错,撤回,再试错. 一点点加油进步呢.
  • 25
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值