代码随想录算法训练营第十九天|回溯算法理论基础、77. 组合、216.组合总和III、17.电话号码的字母组合

正式开始回溯算法!

回溯算法理论基础

回溯算法又叫回溯搜索法,是一种搜索的方式。回溯是递归的副产物,有递归就会有回溯。

回溯法的本质是穷举,所以它本身并不高效。什么问题一定需要回溯呢?

1. 组合问题,n个数里面取k个数的集合

2. 切割问题,一个字符串有几种方式切割

3. 子集问题,一个N个数的集合里有多少符合条件的子集

4. 排列问题,n个数按一定规律全排列

5. 棋盘问题,N皇后,解树独

回溯法解决的问题都可以抽象为树形结构,因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度就构成了树的深度

回溯三部曲:

1. 寻找传入参数和返回值

2. 寻找终止条件

3. 回溯搜索的遍历过程

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

大家可以从图中看出for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了。

77. 组合

每次从集合中选取元素,可选择的范围随着选择的进行而收缩,调整可选择的范围

图中可以发现n相当于树的宽度,k相当于树的深度

那么如何在这个树上遍历,然后收集到我们要的结果集呢?

图中每次搜索到了叶子节点,我们就找到了一个结果

相当于只需要把达到叶子节点的结果收集起来,就可以求得 n个数中k个数的组合集合。

1. 在每一次遍历的时候,除了n和k,还需要一个参数,为int型变量startIndex,这个参数用来记录本层递归的中,集合从哪里开始遍历(集合就是[1,...,n] )。

2. 终止条件。path这个数组的大小如果达到k,说明我们找到了一个子集大小为k的组合了。

3. for循环用来横向遍历,递归的过程是纵向遍历。

class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(int n, int k, int index){
        if(path.size()==k){
            result.push_back(path);
            return;
        }
        for(int i=index; i<=n; 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;
    }
};

剪枝可以把for循环改成:

for(int i=index; i<=n-(k-path.size()); i++)

216.组合总和III

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(int n, int k, int startindex){
        if(path.size()==k){
            int sum = 0;
            for(int i=0; i<k; i++){
                sum+=path[i];
            }
            if(sum == n){
                result.push_back(path);
            }
            return;
        }
        for(int j =startindex; j<=9; j++){
            path.push_back(j);
            backtracking(n, k, j+1);
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum3(int k, int n) {
        result.clear();
        path.clear();
        backtracking(n, k, 1);
        return result;
    }
};
  • 时间复杂度: O(n * 2^n)
  • 空间复杂度: O(n)

17.电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母

要解决如下三个问题:

  1. 数字和字母如何映射
  2. 两个字母就两个for循环,三个字符我就三个for循环,以此类推,然后发现代码根本写不出来
  3. 输入1 * #按键等等异常情况

通过画图可以更好理解:

class Solution {
private:
    const string lettermap[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public:
    vector<string> result;
    string s;
    void backtrack(const string& digits, int index){
        if(index==digits.size()){
            result.push_back(s);
            return;
        }
        int digit = digits[index]-'0';
        string letter = lettermap[digit];
        for(int i=0; i<letter.size(); i++){
            s.push_back(letter[i]);
            backtrack(digits, index+1);
            s.pop_back();
        }
    }
    vector<string> letterCombinations(string digits) {
        result.clear();
        s.clear();
        if (digits.size() == 0) {
            return result;
        }
        backtrack(digits, 0);
        return result;
    }
};

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值