代码随想录算法训练营第二十五天|LeetCode 216.组合总和III、17.电话号码的字母组合

216.组合总和III

题目描述:
找出所有相加之和为 nk 个数的组合,且满足下列条件:

  • 只使用数字1到9
  • 每个数字 最多使用一次

返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

示例一:

输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。

示例二:

输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
解释:
1 + 2 + 6 = 9
1 + 3 + 5 = 9
2 + 3 + 4 = 9
没有其他符合的组合了。

提示:

输入: k = 4, n = 1
输出: []
解释: 不存在有效的组合。
在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。

解题思路:

  1. 关键词提取:k个数、和为n、数字只能用一次、数字1-9、去重、任意顺序
  2. 回溯解法:
    1. 层数k,在达到k层时,将结果保存
    2. 选值范围[1,9],由for循环做遍历寻值
    3. 每个数字(每个值)只能用一次,需要用index去记录起始位置,每层从起始位置开始寻值
    4. 累加的值需要传递,但是换个思路,可以用累减,那么只需要每次递归的时候,更新n的值就可以

代码如下:

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    // 递归函数
    void backtracking(int k, int n, int startIndex) {
        // 递归终止条件
        if(path.size() == k) {
            // 符合条件,保存结果
            if(n == 0) {
                result.push_back(path);
            }
            return;
        }
        // 横向遍历取值
        for(int i = startIndex; i <= 9; i++) {
            // 当前值小于或等于n,可以取值
            if(i <= n) {
                // 保存当前值
                path.push_back(i);
                // 递归,传递的n值,需要减去当前值
                backtracking(k, n - i, i + 1);
                // 回溯
                path.pop_back();
            }
            // 超过n,就结束寻值,后面的值都会超过n
            else {
                break;
            }
        }
    }
public:

    vector<vector<int>> combinationSum3(int k, int n) {
        result.clear();
        path.clear();
        backtracking(k, n, 1);
        return result;
    }
};

总结:

  1. 二刷,采用回溯算法的模板去做,逻辑很清晰。

    1. 解题:组合类,元素不能重复,任意顺序。满足回溯法的前提条件。
    2. 模板:
      1、确认递归函数
      2、确认递归终止条件
      3、确认结果集的存数逻辑
      4、确认单层遍历的起始位置
      5、确认单次结果取数逻辑
      6、确认递归函数的起始状态
      7、确认单次递归的回溯逻辑
  2. 代码随想录讲解的很详细,有兴趣的小伙伴可以去研究一下。

    1. 文字讲解版:https://programmercarl.com/0216.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8CIII.html#%E6%80%9D%E8%B7%AF
    2. 建议:假如按照这个框架执行,比较吃力的话,还是要画一个图出来,按照图去填充代码框架,会更好理解一些。
    3. 在二叉树的递归求子树的累加和时,carl哥有提到这个思路,就是用减法取代加法。
      1. 目标值是一个和,在每次递归中做累加,还需要再去传递一个sum值存储累加值。最终目标值是sum,储存结果集的时候,判断条件是sum==target。
        sum=1+2+3+…+n
      2. 目标值是一个和,在每次递归中做累减,只需要每次更新目标值就可以。最终目标值是0,储存结果集的时候,判断条件是0==target-sum。
        ?=target-1-2-3-…-n

17.电话号码的字母组合

题目描述:
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例一:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

示例二:

输入:digits = "2"
输出:["a","b","c"]

提示:

0 <= digits.length <= 4
digits[i] 是范围 ['2', '9'] 的一个数字。

解题思路:

  1. 关键词提取:‘2’-'9’字符串、字母组合、任意顺序、字符串映射
  2. 回溯解法:
    1. 层数len是字符串的长度,一共有多少个字符"23"
    2. 在达到len层时,将结果保存
    3. 需要将字符串中"23"的每一位字符(数字)‘2’、'3’单独拷贝下来,映射为“abc”这样的字符串,拷贝下来,做遍历
    4. 选值范围[a,c]或[d,f]…[wxyz],由字符串的每位字符决定是哪个选值范围,如’2’对应[a,c],'3’对应[d,f]
    5. 由for循环在上述选值范围中做遍历寻值[a,c]
    6. 对于字符串的每一位字符(数字)“23”,采用index参数保存当前的位数,每层递归,index参数移动至下一位,index+1。如index初始化是0,对应’2’,index+1=1,对应’3’

代码如下:

class Solution {
private:
    const string letterMap [10] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    vector<string> result;
    string path;
public:
    // 递归
    void backtracking(string digits, int index) {
        // 终止条件:层数达到字符串的长度
        if(index == digits.size()) {
            // 保存结果集
            result.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(digits, index + 1);
            // 回溯
            path.pop_back();
        }
    }
    vector<string> letterCombinations(string digits) {
        if(digits.size() == 0) {
            return result;
        }
        backtracking(digits, 0);
        return result;
    }
};

总结:

  1. 三刷,采用回溯算法的模板去做,逻辑很清晰。

    1. 解题:组合类,元素不能重复,任意顺序。满足回溯法的前提条件。
    2. 模板:
      1、确认递归函数
      2、确认递归终止条件
      3、确认结果集的存数逻辑
      4、确认单层遍历的起始位置
      5、确认单次结果取数逻辑
      6、确认递归函数的起始状态
      7、确认单次递归的回溯逻辑
  2. 代码随想录讲解的很详细,有兴趣的小伙伴可以去研究一下。

    1. 文字讲解:https://programmercarl.com/0017.%E7%94%B5%E8%AF%9D%E5%8F%B7%E7%A0%81%E7%9A%84%E5%AD%97%E6%AF%8D%E7%BB%84%E5%90%88.html#%E6%80%9D%E8%B7%AF
    2. 不一样的地方是,在分析深度遍历的时候,我没有将字符串写出来,所以在分析过程中会比较模糊,需要做多次的调试与修改。
    3. 建议还是学一下将回溯法题目的解题思路画成图,有利于自己的理解以及代码的编写,可以减少调试修改的次数。特别是对于深度遍历的结束条件以及横向遍历的起始条件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值