LeetCode017:电话号码的字母组合

题目介绍

题目:电话号码的字母组合
描述:给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

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

数字字母映射

示例:

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

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

解析

如果还记得高中学过的排列组合问题,就可以发现以上问题是一个最简单的组合问题。假设输入了 n 个数字,每个数字所包含的字母个数分别是 m1, m2, ..., mn,那么解的个数则为:C1m1C1m2...C1mn ,也就是m1*m2*...*mn。虽然这和我们的题目关系不大,但是它限定了时间复杂度的下限,我们在设计算法时可以参考此值。

首先,我们考虑最简单的情况,假如只输入一个数字 2,那么答案的解集是{"a", "b", "c"},现在,我们输入第二个数字 3,那就是把 "d"、"e"、"f" 分别和 "a" 组合,然后和 "b" 组合,最后再与 "c" 组合。也就是说,我们得到前面的结果,复制多份,每一个结果后都增加一个当前的字符,就可以得到最终结果,由此可以得出第一条思路:递归。参考代码如下:

public List<String> letterCombinations(String digits) {
    if(digits.length()==0)return new ArrayList<>();
    String[] letters = {"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    return letterCombinations(digits,letters,digits.length()-1);
}

private List<String> letterCombinations(String digits,String[] letters, int end) {
    List<String> result = new ArrayList<>();
    if (end==0) {
        int index = digits.charAt(end)-'2';
        for (int i = 0; i < letters[index].length(); i++) {
            result.add(String.valueOf(letters[index].charAt(i)));
        }
        return result;
    }else{
        int index = digits.charAt(end)-'2';
        for (int i = 0; i < letters[index].length(); i++) {
            // 复制前一结果
            List<String> temp = new ArrayList<>(letterCombinations(digits, letters, end-1));
            for (int j = 0; j < temp.size(); j++) {
                // 每一条结果后边都加上当前的一个字符
                temp.set(j, temp.get(j)+letters[index].charAt(i));
            }
            result.addAll(temp);
        }
    }
    return result;
}

但是以上的代码并不完美,为了复制递归需要的数据,额外的执行了一段for循环语句,大大增加了时间复杂度,而且由于无法使用StringBuilder等类来操作字符串的拼接,额外创建了许多String变量,又浪费了大量的时间和空间。所以我们需要寻找一种能够使用StringBuilder,又不需要多一层for循环的方法,这个方法就是:递归+回溯。

现在依然假设输入了数字 2,因为还要输入更多的数字,所以这时还得不到最终结果,我们从数字 2 代表的结果中选择一个字符存入到StringBuilder里,例如选择了字符 'a',现在StringBuilder里的结果就是 'a'。接下来输入数字 3 之后,'d'、'e'、'f' 每个字符都要和 'a' 组合,那么我们可以把 'a' 固定在StringBuilder中,加入 'd' 组成结果 "ad",然后移除 'd' 再加入 'e',组成结果 'ae',...。假如输入了更多数字,就依法炮制,再固定 "ad",然后加入 'g' 组成 "adg",移除 'g' 再加入 'h' 组成 "adh",...。这个过程就是回溯,之所以还有递归是因为每次运算只处理了一个输入的数字。参考代码如下:

public List<String> letterCombinations(String digits) {
    List<String> result = new ArrayList<>();
    if(digits.length()==0)return result;
    String[] letters = {"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    StringBuilder sb = new StringBuilder();
    letterCombinations(digits, 0, letters, sb, result);
    return result;
}

private void letterCombinations(String digits, int start,String[] letters, StringBuilder sb, List<String> result){
    if(start==digits.length()){
        if (sb.length()>0) {
            result.add(sb.toString());
        }
        return;
    }
    if (digits.charAt(start)>='2'&&digits.charAt(start)<='9'){
        for (int i = 0; i < letters[digits.charAt(start)-'2'].length(); i++) {
            // 加入一个字符
            sb.append(letters[digits.charAt(start)-'2'].charAt(i));
            // 进入下一级运算
            letterCombinations(digits, start+1, letters, sb, result);
            // 删除最后加入的字符
            sb.deleteCharAt(sb.length()-1);
        }
    }else{
        letterCombinations(digits, start+1, letters, sb, result);
    }

}

可以看到递归+回溯算法的实现十分简洁,但是也较为抽象。接下来我们就以示例为例,看看输入字符串为 "23" 时,代码是如何执行的。

首先,获取到字符 '2' 时,函数进到第一轮,代码走到for循环中时把字符 'a' 添加到了 sb(StringBuilder实例)对象中,然后就进入了函数的第二轮,for循环将等待此递归函数执行完成后再继续运行。这时获取到字符 '3',又把字符 'd' 添加到了sb中,进入了下一轮的递归,也就是函数的第三轮。

第三轮已经得到了完整的结果,便return了,这时第二轮的函数继续执行,它把 sb 中的最后一个字符删除,于是 sb 中又只剩下 'a',for循环重复这个过程之后,就得到了三个结果,分别是 "ad","ae","af"。

现在,第二轮的函数结束了,sb 中依然只剩下字符 'a',回到第一轮时继续执行删除语句,便把 'a' 也删除了,然后继续处理 'b'、处理 'c',便得到了全部结果。

总结

其实早在研究KMP算法时,我们就见到过回溯的身影,之所以有KMP算法就是为了解决暴力算法的回溯问题,当然不是任何情况下都能避免回溯的。

递归+回溯能够解决很多实际的问题,之后的许多题目都可以用这个思路来解决,但是这个算法较为抽象,需要我们多多练习,才能深刻理解。

相关源码已经上传到我的Github

下题预告

题目:四数之和
描述:给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:

[
    [-1,  0, 0, 1],
    [-2, -1, 1, 2],
    [-2,  0, 0, 2]
] 

我是飞机酱,如果您喜欢我的文章,可以关注我~

编程之路,道阻且长。唯,路漫漫其修远兮,吾将上下而求索。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容

  • 前言 最先接触编程的知识是在大学里面,大学里面学了一些基础的知识,c语言,java语言,单片机的汇编语言等;大学毕...
    oceanfive阅读 3,023评论 0 7
  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,301评论 0 2
  • 关于数据的存储结构,以下选项描述正确的是( D )A: 数据所占的存储空间量B: 存储在外存中的数据C: 数据在计...
    IIronMan阅读 136,035评论 7 60
  • 觉得生活之中最揣摩不定的是人心,表面现象与本质存在着天然差别,表面看着很美好的其实在深处也会有污秽的一面。如果把心...
    阑十三阅读 393评论 0 11
  • 漫画17部。排序按内容特点或个人评价,标题蓝色高亮带链的曾在个人微博或@推遍天下 即时写过评价,可搜索。 1. 《...
    风华布衣阅读 539评论 0 0