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

216.组合总和III

题目链接:link
文章讲解:link
视频讲解:link

一、做题感受&第一想法

直接套用“组合”问题的回溯框架,在找到符合规格的组合后判断它们的和是否符合题目。如果符合则记录,不符合则直接返回。

但是没有剪枝。

class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;

    int sum(vector<int> path){ //计算path中元素之和
        int sum = 0;
        for(int i = 0;i < path.size();i++){
            sum += path[i];
        }
        return sum;
    }

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

    void backtracking(int k, int n, int startIndex){
        if(path.size() == k){ //已经找到size为k的组合
            if(sum(path) == n) result.push_back(path); //如果元素之和为n,则符合题意,收集结果
            return; //如果元素之和不为n,返回。
        }
        for(int i = startIndex; i <= 9; i++){
            path.push_back(i);
            backtracking(k,n,i+1);
            path.pop_back();
        }
        return;
    }
};

二、学习文章后收获

1.剪枝的代码

  • for循环的范围,可以剪枝
  • 当path.size()还未到达n,但sum已经大于k时,可以剪枝
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;

    vector<vector<int>> combinationSum3(int k, int n) {
        int sum = 0;
        backtracking(k,n,sum,1);
        return result;
    }

    void backtracking(int k, int n, int sum, int startIndex){
        if(path.size() == k && sum == n){
            result.push_back(path);
            return;
        }
        else if(path.size() == k && sum != n){
            return;
        }
        else if(sum > n){ //剪枝
            return;
        }

        for(int i = startIndex; i <= 9 - (k - path.size()) + 1 ; i++){ //剪枝
            path.push_back(i);
            sum += i;
            backtracking(k,n,sum,i+1);
            sum -= i;
            path.pop_back();
        }
        return;
    }
};

三、过程中遇到的问题

1.回溯操作要做完整

要观察清楚哪些变量需要回溯。

下面代码中,sum没有被回溯,导致错误。

        for(int i = startIndex; i <= 9 - (k - path.size()) + 1 ; i++){ //剪枝
            path.push_back(i);
            sum += i;
            backtracking(k,n,sum,i+1);
            //没有回溯sum!!!
            path.pop_back();
        }

2.时间、空间复杂度

时间:O(2^n)
空间:O(n)

???why???


17.电话号码的字母组合

题目链接:link
文章讲解:link
视频讲解:link

一、做题感受&第一想法

一开始理解错了题意。我以为是:给出从2-9的某个范围,然后返回所有可能的数字排列对应的所有可能字母排列。
实际上是指,在给定的数字序列中,返回所有可能的字母排列。

分析回溯三要素:

  • 函数参数:一定要能标明该层回溯的“集合”范围! 本题中,给出数字序号index,即可根据字符字典dictionary找到对应的字符集合
  • 层内逻辑:用for循环遍历数字对应的字符集合,从而“开枝散叶”。
  • 返回条件:当str长度等于数字串长度,则收集结果(注意数字串长度为0、str为空的情况,不要收集。)(或者,通过main函数控制进入回溯的数字串一定不为空)
class Solution {
public:
    vector<int> digits_int;
    vector<string> result;
    vector<string> dictionary;
    string str;

    void initDict(){
        dictionary.push_back(" ");
        dictionary.push_back(" ");
        dictionary.push_back("abc");
        dictionary.push_back("def");
        dictionary.push_back("ghi");
        dictionary.push_back("jkl");
        dictionary.push_back("mno");
        dictionary.push_back("pqrs");
        dictionary.push_back("tuv");
        dictionary.push_back("wxyz");
        return;
    }
    vector<string> letterCombinations(string digits) {
        initDict();
        for(int i = 0; i < digits.size();i++){
            digits_int.push_back(digits[i]-'0');
        }
        backtracking(0);
        return result;
    }
    void backtracking(int index){ //index是本层处理的数字的序号
        if(index == digits_int.size()){
            if(!str.empty()) result.push_back(str);
            return;
        }
        for(int i = 0;i < dictionary[digits_int[index]].size();i++){
            int size = str.size();
            str += dictionary[digits_int[index]][i];
            backtracking(index + 1);
            str.resize(size);
        }
        return;
    }
};

二、学习文章后收获

1.面对电话号码的字母组合问题,如何存储“字典”

//方法一:string数组
    string letter[10] = { //存储映射关系
        "",
        "",
        "abc",
        "def",
        "ghi",
        "jkl",
        "mno",
        "pqrs",
        "tuv",
        "wxyz"
    };
    
//方法二:vector<string>
    vector<string> dictionary; //字典
    
    void initDict(){
        dictionary.push_back(" ");
        dictionary.push_back(" ");
        dictionary.push_back("abc");
        dictionary.push_back("def");
        dictionary.push_back("ghi");
        dictionary.push_back("jkl");
        dictionary.push_back("mno");
        dictionary.push_back("pqrs");
        dictionary.push_back("tuv");
        dictionary.push_back("wxyz");
        return;
    }

2.代码随想录上代码(可以跳过,基本思路一样)

class Solution {
public:
    string letter[10] = { //存储映射关系
        "",
        "",
        "abc",
        "def",
        "ghi",
        "jkl",
        "mno",
        "pqrs",
        "tuv",
        "wxyz"
    };
    vector<string> result;
    string str;

    vector<string> letterCombinations(string digits) {
        if(digits.size()) backtracking(0,digits);
        return result;
    }

    void backtracking(int index, string digits){
        if(index == digits.size()){
            result.push_back(str);
            return;
        }
        int digit = digits[index] - '0';
        string strTemp= letter[digit];

        for(int i = 0;i < strTemp.size();i++){
            str += strTemp[i];
            backtracking(index + 1, digits);
            str.pop_back();
        }
        return;
    }
};

3.一些思考

本题和”组合“问题的区别??

  • 组合问题:在同一个集合中求组合
  • 本题:针对每一个数字有不同集合,也就是求不同集合间的组合。

4.面试中尽量考虑边界条件

注意:输入1 * #按键等等异常情况(可通过字符转换为数字时,限制ASIIC码的范围)

代码中最好考虑这些异常情况,但题目的测试数据中应该没有异常情况的数据,所以我就没有加了。

但是要知道会有这些异常,如果是现场面试中,一定要考虑到!

5.时间、空间复杂度

时间:O(3n * 4m)

空间:O(3n * 4m)

6.如果把回溯过程放入递归函数里

    vector<string> result;

    vector<string> letterCombinations(string digits) {
        string str;
        if(digits.size()) backtracking(0,digits,str);
        return result;
    }

    void backtracking(int index, string digits,string str){ //还要传入str
        if(index == digits.size()){
            result.push_back(str);
            return;
        }
        int digit = digits[index] - '0';
        string strTemp= letter[digit];

        for(int i = 0;i < strTemp.size();i++){
            backtracking(index + 1, digits, str + strTemp[i]); //注意这里的不同!!
            //之后不用处理str,因为str没变
        }
        return;
    }
};

三、过程中遇到的问题

1.string是可以push_back和pop_back的!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值