LeetCode 17 电话号码的字母组合(字符串、回溯算法Backtracking、递归Recursive)

题目要求:

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

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

示例:

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

说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。



 

 

C++代码:

class Solution {
public:
    vector<string> letterCombinations(string digits) {
        vector<string> nums={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
        vector<string> ans;
        if(digits.size()==0){return ans;}
        combinations(digits,nums,ans,"",0);
        return ans;
    }
    
    void combinations(string digits,vector<string> nums,vector<string>& ans,string temp,int start){
        if(start>=digits.size()){ans.push_back(temp); return;}
        int index=digits[start]-'0';
        for(int i=0;i<nums[index].size();++i){
            combinations(digits,nums,ans,temp+nums[index][i],start+1);
        }
        return;
    }
};

 

 

结果(0ms,beat100%,真强大):

 

 

 

解析:

这里主要用到了回溯算法的思想,首先简单介绍一下什么是回溯算法。

回溯算法(Backtracking)实际上是一个类似枚举的深度优先搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回到上一步还能执行的状态,尝试别的路径,类似于走迷宫一样。每次退回就是每次的回溯,所以回溯法要保存每一次的状态。

回溯算法也是一种选优搜索法,按照选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或者达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”
其实就是深度优先搜索的策略,从根结点出发深度搜索。当探索到某一节点时,要先判断该节点是否包含问题的解,如果包含,就从该节点出发继续探索下去,若该节点不包含问题的解,则逐层向其祖先节点回溯。

若用回溯法求问题的所有解时,要回溯到根,且跟节点的所有可行的子树都要已被搜索遍才结束

 

 

首先在原来的letterCombinations函数里,我们先定义一个字符串类型的vector容器nums,按照电话号码从0到9存储相应的字符串,比如0和1是空的,就存"",然后数字2就是"abc",一直到数字9

vector<string> nums={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};

然后再定义一个nums的字符串类型的vector容器ans,用于存放最后要返回的字母组合

vector<string> ans;

下面先做一个初始的判断,如果要判断的digits字符串为空,也就是digits.size()==0,则直接返回当前的ans即可,因为当前的ans也是空的

if(digits.size()==0){return ans;}

然后就开始调用我们的回溯算法combinations,后面会介绍。

直到找完全部的正确组合,return ans即可。

 

那么这里重点就是如何设计回溯算法combinations

combinations函数里有一些参数介绍下,后面会用到:比如需要进行转换的digits、前面定义的nums、用于输出的ans、用于存储中间转换好的临时变量temp以及strat变量。

首先拿start跟digits.size()做比较,因为后面start会+1,如果start大于等于了digits.size(),那就把转换好的临时变量temp给push_back进ans里,然后return,回溯。

if(start>=digits.size()){ans.push_back(temp); return;}

具体我们通过举例来说明,会比较清晰,比如就题目的实例:当digits为"23"时,也就是数字2对应的字母有a、b、c,而数字3对应的字母有d、e、f,所以3×3就有9种组合,也就是"ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"

因为一开始combinations函数里temp="",然后start=0

combinations(digits,nums,ans,"",0);

先进行判断,start=0并没有大于等于digits.size()为2,所以不执行{}里面的内容

if(start>=digits.size()){ans.push_back(temp); return;}

然后我们就要去取得digits里面每一位的字符所代表的数字,因为后面需要用它作为索引,从nums里面取出该数字所对应的字母。所以这里定义int变量index=digits[start]-'0',也就是index=digits[0]='2'-'0',然后取了int,就是整数2了

int index=digits[start]-'0';

然后我们就要去取nums里数字2对应哪些字母,统统遍历一遍,用一个for循环,i从0开始遍历到nums[index].size(),也就是nums[2]字符串里包含的字符的个数,对应"abc",也就是3个

for(int i=0;i<nums[index].size();++i)

然后for循环里,我们对combinations进行迭代,有2个地方不同,第一个就是这里的temp要加上nums[index][i],也就是加上当前index和i对应的nums里面的字母,此视nums[index][i]=nums[2][0],也就是字母a,然后因为一开始temp为空,所以这一次temp+nums[index][i]结果就是'a'。然后把它当作新的temp进行下一次的combinations迭代。

还有一个不同就是start要记得+1,因为这样我们才行到进行判断,从而回溯。

combinations(digits,nums,ans,temp+nums[index][i],start+1);

比如,这里重新迭代combinations,然后temp此时是a了,然后start+1后就变成1了,然后继续判断,start=1还是没超过digits.size()为2,所以不执行{}里的内容

if(start>=digits.size()){ans.push_back(temp); return;}

然后这是index就是digits[start]-'0'也就是digits[1]-'0',即'3'-'0',取整数就是数字3

int index=digits[start]-'0';

里面又嵌套一个循环,nums[3]包括"def"3个字母

for(int i=0;i<nums[index].size();++i)

然后再迭代,这时候temp+nums[index][i]也就是上一次的'a'加上这时的nums[index][i]=nums[3[0]即'd',所以此时的temp就是'ad'了,然后start+1,就变成2了

combinations(digits,nums,ans,temp+nums[index][i],start+1);

因此这时候start+1变成2了,所以满足下面的条件start>=digits.size(),就把当前的临时变量temp即'ad'给push_back加入ans里面,然后return回溯到上一步,也就是没有加上'd',即temp还是'a',然后start还是1的时候

if(start>=digits.size()){ans.push_back(temp); return;}

之后同理把 "ae", "af"也push_back加入ans里后,不满足条件,所以有回溯上一步,即temp还是空,然后for循环i=1,这时候第一个字母就是b,然后继续一次迭代,得到"bd", "be", "bf",然后c的也同理,得到 "cd", "ce", "cf"

所以最后就得到了9种组合,也就是"ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"

最后return ans即可。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章