目录

题目描述

回溯解法

利用队列求解


题目描述

题目地址:https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/

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

示例:
输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。

回溯解法

这道题的解法是用回溯的方式,在循环里面套了递归调用。本来递归就不好理解了,再加上循环的递归,就更难理解了。
我们先不考虑递归,先看看下面这个问题怎么解决。
假设输入是2,只有一个字符,那么应该怎么解呢?
按照题目要求2=“abc",所以结果应该是["a","b","c"]
先不用想着怎么去写递归,只思考下怎么打印出这个结果
这个太简单了,一个循环就搞定了:

result = List()
for(i=0;i<len("abc");i++) {tmp = iresult.add(tmp)
}
return result

上面是伪代码,一个循环就搞定了。

如果输入的是23,应该怎么做呢?23的结果是["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"],我们仍然不考虑怎么去写递归,只是考虑怎么把这个结果给弄出来。代码如下:

result = List()
for(i=0;i<len("abc");i++) {for(j=0;j<len("def");j++)tmp = i+jresult.add(tmp)
}
return result

也就是说23这样的长度为2的字符串可以用两层循环搞定。
如果输入的是234呢,仍然不要考虑怎么去写递归,而是想怎么把结果打印出来。

result = List()
for(i=0;i<len("abc");i+=1) {for(j=0;j<len("def");j+=1) {for(k=0;k<len("ghi");k+=1) {tmp = i+j+kresult.add(tmp)}}
}
return result

这次用了三层循环。
如果输入的是2345,那么代码可以这么写:

result = List()
for(i=0;i<len("abc");i+=1) {for(j=0;j<len("def");j+=1) {for(k=0;k<len("ghi");k+=1) {for(n=0;n<len("jkl");n+=1)tmp = i+j+k+nresult.add(tmp)}}
}
return result

这次是用了四层循环。现在是不是能看出一些门道了?对的。循环的嵌套层数,就是输入的字符串长度。输入的字符串长度是1,循环只有1层。
输入的字符串长度是3,循环就是3层。如果输入的字符串长度是10,那么循环就是10层。
可是输入的字符串长度是不固定的,对应的循环的嵌套层数也是不固定的,那这种情况怎么解决呢?这时候递归就派上用场了。

对于打印"2345"这样的字符串:
第一次递归就是上图中最下面的方格,然后处理完第一个字符2之后,将输入的字符改变成"345"并调用第二个递归函数
第二次递归处理3,将字符串改变成"45"后再次递归
第三次递归处理4,将字符串改变成"5"后继续递归
第四次递归处理5,将字符串改变成""后继续递归
最后发现字符串为空了,将结果放到列表中并返回
上面是从函数调用的角度去看的,而每次调用下一层递归时,都需要将本层的一些处理结果放到一个临时变量中,再传递给下一层,从这个变量层层传递的变化看,就像一棵树一样,这个算法的时间复杂度很高,是O(3^n)这个级别的,空间复杂度是O(n)

动态图如下:

java代码实现:

class Solution {//一个映射表,第二个位置是"abc“,第三个位置是"def"。。。//这里也可以用map,用数组可以更节省点内存String[] letter_map = {" ","*","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};public List<String> letterCombinations(String digits) {//注意边界条件if(digits==null || digits.length()==0) {return new ArrayList<>();}iterStr(digits, "", 0);return res;}//最终输出结果的listList<String> res = new ArrayList<>();//递归函数void iterStr(String str, String letter, int index) {//递归的终止条件,注意这里的终止条件看上去跟动态演示图有些不同,主要是做了点优化//动态图中是每次截取字符串的一部分,"234",变成"23",再变成"3",最后变成"",这样性能不佳//而用index记录每次遍历到字符串的位置,这样性能更好if(index == str.length()) {res.add(letter);return;}//获取index位置的字符,假设输入的字符是"234"//第一次递归时index为0所以c=2,第二次index为1所以c=3,第三次c=4//subString每次都会生成新的字符串,而index则是取当前的一个字符,所以效率更高一点char c = str.charAt(index);//map_string的下表是从0开始一直到9, c-'0'就可以取到相对的数组下标位置//比如c=2时候,2-'0',获取下标为2,letter_map[2]就是"abc"int pos = c - '0';String map_string = letter_map[pos];//遍历字符串,比如第一次得到的是2,页就是遍历"abc"for(int i=0;i<map_string.length();i++) {//调用下一层递归,用文字很难描述,请配合动态图理解iterStr(str, letter+map_string.charAt(i), index+1);}}
}

python代码实现:

class Solution(object):def letterCombinations(self, digits):""":type digits: str:rtype: List[str]"""# 注意边界条件if not digits:return []# 一个映射表,第二个位置是"abc“,第三个位置是"def"。。。# 这里也可以用map,用数组可以更节省点内存d = [" ","*","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"]# 最终输出结果的listres = []# 递归函数def dfs(tmp,index):# 递归的终止条件,注意这里的终止条件看上去跟动态演示图有些不同,主要是做了点优化# 动态图中是每次截取字符串的一部分,"234",变成"23",再变成"3",最后变成"",这样性能不佳# 而用index记录每次遍历到字符串的位置,这样性能更好if index==len(digits):res.append(tmp)return# 获取index位置的字符,假设输入的字符是"234"# 第一次递归时index为0所以c=2,第二次index为1所以c=3,第三次c=4# subString每次都会生成新的字符串,而index则是取当前的一个字符,所以效率更高一点c = digits[index]# map_string的下表是从0开始一直到9, ord(c)-48 是获取c的ASCII码然后-48,48是0的ASCII# 比如c=2时候,2-'0',获取下标为2,letter_map[2]就是"abc"letters = d[ord(c)-48]# 遍历字符串,比如第一次得到的是2,页就是遍历"abc"for i in letters:# 调用下一层递归,用文字很难描述,请配合动态图理解dfs(tmp+i,index+1)dfs("",0)return res

利用队列求解

我们可以利用队列的先进先出特点,再配合循环完成题目要求。
我们先将2对应的字符"a","b","c"依次放入队列中

之后再从队列中拿出第一个元素"a",跟3对应的字符"d","e","f"挨个拼接

于是队列就变成了下面这个样子:

按照同样的方式,再将"b"从队列中拿出,再跟3对应的字符"d","e","f"挨个拼接,队列又变成下面这个样子:

动态演示如下:

java代码实现:

class Solution {public List<String> letterCombinations(String digits) {if(digits==null || digits.length()==0) {return new ArrayList<String>();}//一个映射表,第二个位置是"abc“,第三个位置是"def"。。。//这里也可以用map,用数组可以更节省点内存String[] letter_map = {" ","*","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};List<String> res = new ArrayList<>();//先往队列中加入一个空字符res.add("");for(int i=0;i<digits.length();i++) {//由当前遍历到的字符,取字典表中查找对应的字符串String letters = letter_map[digits.charAt(i)-'0'];int size = res.size();//计算出队列长度后,将队列中的每个元素挨个拿出来for(int j=0;j<size;j++) {//每次都从队列中拿出第一个元素String tmp = res.remove(0);//然后跟"def"这样的字符串拼接,并再次放到队列中for(int k=0;k<letters.length();k++) {res.add(tmp+letters.charAt(k));}}}return res;}
}

python代码实现:

class Solution(object):def letterCombinations(self, digits):""":type digits: str:rtype: List[str]"""   if not digits:return []# 一个映射表,第二个位置是"abc“,第三个位置是"def"。。。# 这里也可以用map,用数组可以更节省点内存d = [" ","*","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"]# 先往队列中加入一个空字符res = [""]for i in digits:size = len(res)# 由当前遍历到的字符,取字典表中查找对应的字符串letters = d[ord(i)-48]# 计算出队列长度后,将队列中的每个元素挨个拿出来for _ in xrange(size):# 每次都从队列中拿出第一个元素tmp = res.pop(0)# 然后跟"def"这样的字符串拼接,并再次放到队列中for j in letters:res.append(tmp+j)return res

欢迎扫描关注公众号 有更多图解的算法面试题等你哦~

【图解回溯面试题】facebook面试题-电话号码的字母组合(两种解法)相关推荐

  1. Java常见面试题:对象的访问定位的两种方式

    对象的访问定位的两种方式 java对象在访问的时候,我们需要通过java虚拟机栈的reference类型的数据去操作具体的对象. 由于reference类型在java虚拟机规范中只规定了一个对象的引用 ...

  2. 面试题:基于Python的青蛙跳台阶两种解法

    问题 一只青蛙要跳上 n 层高的台阶,一次能跳一级,也可以跳两级,请问这只青蛙有多少种跳上这个 n 层高台阶的方法? 递归 设青蛙跳上 n 级台阶有 f(n)种方法,把这 n 种方法分为两大类,第一种 ...

  3. leetcodeT14-最长公共前缀(两种解法+图解)

    文章目录 题目:最长公共前缀 法一:暴力解法 思路 图解 代码 法二:两两对比法 思路 图解 代码 题目:最长公共前缀 编写一个函数来查找字符串数组中的最长公共前缀. 如果不存在公共前缀,返回空字符串 ...

  4. [Leetcode][第17题][JAVA][电话号码的字母组合][回溯]

    [问题描述][中等] [解答思路] 用哈希表/数组存储每个数字对应的所有可能的字母,然后进行回溯操作. 回溯过程中维护一个字符串,表示已有的字母排列(如果未遍历完电话号码的所有数字,则已有的字母排列是 ...

  5. 一道大厂Python面试题,4种解法,从青铜到王者引发的“思考”!

    "菜鸟学Python",第"518"篇原创 金九银十应该是校招的黄金时间,大家都知道去大厂面试,算法是一个必须要过的关,无论是笔试还是面试的环节都需要.而算法这 ...

  6. LeetCode打卡--Facebook面试题

    文章目录 Facebook面试题 LeetCode 125. Valid Palindrome LeetCode 88. Merge Sorted Array LeetCode 278. First ...

  7. java linux 面试题_java 面试题

    第一,谈谈final, finally, finalize的区别. final?修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承.因此一个类不能既被声明为 ...

  8. 面试ASP.NET程序员的笔试题和机试题(转,有点意思,答案不是很标准)

    面试 一般会叫你填两个表 1个是你的详细信息表 1个是面试题答卷 两个都要注意反正面是否都有内容不要遗漏,如果考你机试一般也有两种,就是程序连接数据库或一些基本的算法(二分查找,递归等),公司一般都是 ...

  9. 面试ASP.NET程序员的笔试题和机试题

    面试 一般会叫你填两个表 1个是你的详细信息表 1个是面试题答卷 两个都要注意反正面是否都有内容不要遗漏,如果考你机试一般也有两种,就是程序连接数据库或一些基本的算法(二分查找,递归等),公司一般都是 ...

最新文章

  1. 优化以insert语句导入数据的方式
  2. 党在心中(turtle画图)
  3. 12 个月 vs. 1 个月,你的年终奖是多少?
  4. 联想(ThinkServer) RD650做硬件 raid5 配置
  5. 《锁王创造营》 第一关:初出茅庐
  6. 端游网易我的世界服务器未响应,我的世界 PC 端游戏启动异常处理指引(2018.09.21)...
  7. 牛客网刷题记录——数理统计(累积记录)
  8. wine android模拟器,Mac  下运行window 软件,(wine 模拟器)
  9. WinForm下多层架构的实现
  10. 字符串首尾空格去除问题
  11. 一站式:虚拟机安装Linux系统(CentOS_7),多节点Linux环境打造,XShell的使用
  12. 给初入测试/开发程序员的几点建议,把困难当做猎物......
  13. tailwind css的使用
  14. 固态硬盘SSD学习笔记:SSD主控
  15. BUUCTF:zip
  16. FLP不可能原理(转)
  17. Spring框架概述
  18. table 中的col和colgroup
  19. 配置OPTIONS请求
  20. 张驰课堂:六西格玛培训工具——箱线图

热门文章

  1. MySQL分库分表设计
  2. AE基础教程(8)——第8章 摄像机视图
  3. 循环神经网络与LSTM
  4. 透过OpenStack Ocata贡献排名看中国力量强势崛起
  5. 30自学java之路_从小白到精通的java自学之路
  6. 不得不学习的MybatisPlus教程
  7. eclipse无法启动报错,会弹出配置信息的提示
  8. heroes 2 android,真爱英雄2弹性球(Gachi Heroes 2 Flexboll)游戏-Gachi Heroes 2 Flexboll手机版预约-乐游网安卓...
  9. 项目时间管理(IT项目版)
  10. 京东万象数据接口,钱没花完,接口404,客服是白痴,无法维权