电话号码的字母组合(不同集合)--回溯问题

题目描述

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

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

输入与输出样例

示例 1:

输入:digits = "23"

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

示例 2:

输入:digits = ""

输出:[]

示例 3:

输入:digits = "2"

输出:["a","b","c"]

来源:力扣(LeetCode)

链接:https://leetcode.cn/problems/letter-combinations-of-a-phone-number

算法描述

对于这一道题目,也是求组合的问题。之前我们已经做过了一道组合的问题,具体大家可以看一下我的这一篇博客 组合(同一集合)--经典回溯问题。我们知道了使用回溯法可以解决n个for循环的问题。比如,对于样例1,我们就可以使用两层for循环来解决问题。

class Solution {
public:
    sting str1 = "abc";
    string str2 = 'def';
    string path;
    vector<string> ans;
    for(int i = 0; i < str1.size(); i++){
        path.push_back(str1[i]);
        for(int j = 0; j < str2.size(); j++){
            path.push_back(str2.[j]);
            ans.push_back(path);
            path.pop_back();
        }
        path.pop_back();
    }
    return ans;
}

但是这样的方法在组合数的数量变多时,需要的for循环也就越多,时间复杂度不说,写起来就受不了。所以我们引入了回溯的方法,通过使用递归来代替for循环的嵌套。但是这道题目与之前那道题目不同的是:这题是在不同的集合之间求组合,而那道题是在同一集合内求组合。因为每一个数码对应的集合是不同的

确定数码与字母的映射关系

首先表示出每个数码与对应字母之间的映射关系并不难,我们可以通过map关联容器或者使用字符串数组来实现,这里我选择使用字符串数组来实现。

const string Yin[10] = {
        " ",  // -- 0
        " ",  // -- 1
        "abc",// -- 2
        "def",// -- 3 
        "ghi",// -- 4
        "jkl",// -- 5
        "mno",// -- 6
        "pqrs",//-- 7
        "tuv",// -- 8
        "wxyz"// -- 9
        //下标表示对应的数码
    };
确定回溯的基本框架(边界条件,单层迭代集合,递归参数)

其次就是如何回溯了,先使用回溯三部曲把基本的框架构建出来。

    void backstracking(参数){
        if(边界条件){
            收集结果
        }
        
        加入判断进行剪枝优化
        
        for(横向迭代对应的集合){
            相应的处理
            backstracking(下一层的参数); // 利用递归实现进入下一层循环的作用
            回溯处理
        }
    }

首先确定边界条件,也就是当递归的结果字符串的size == 电话号码的位数时即可。

其次就是单层迭代的集合是什么?也就是每个数码 i 对应的字母 Yin[ i ]。

在这个过程中,我们需要知道电话号码的位数,其次我们还需要知道现在是第几位电话号码对吧,这样才能够取得每一位对应的字母,也就是每一层递归对应的横向迭代集合。

对应的代码就可以写出来啦@~@

class Solution {
public:
    const string Yin[10] = {
        " ",  // -- 0
        " ",  // -- 1
        "abc",// -- 2
        "def",// -- 3 
        "ghi",// -- 4
        "jkl",// -- 5
        "mno",// -- 6
        "pqrs",//-- 7
        "tuv",// -- 8
        "wxyz"// -- 9
    };
    string path;
    vector<string> result;
    void backstracking(const string& digits, int index){
        if(path.size() == digits.size()){  //  判断边界条件
            result.push_back(path);
            return;
        }
        int num = digits[index] - '0';  
        string temp = Yin[num];    //  取得该层的横向迭代集合
        for(int i = 0; i < temp.size(); i++){  //  横向迭代
            path.push_back(temp[i]);
            backstracking(digits, index + 1);  //  进入下一层for循环
            path.pop_back();  //  回溯
        }
    }
    vector<string> letterCombinations(string digits) {
        if(!digits.size()){
            return result;
        }
        backstracking(digits, 0);
        return result;
    }
};

大家可以再翻到上面的for循环版本对照的看一下,可以发现二者的思路是完全一样的,不过回溯使用递归取代了for循环,具体来说,它是使用了一层递归来等效代替一层for循环。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PL_涵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值