leetcode刷题日记-【17. 电话号码的字母组合】

114 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情

题目描述

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

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

image.png

 

示例 1:

输入:digits = "23" 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"] 示例 2:

输入:digits = "" 输出:[] 示例 3:

输入:digits = "2" 输出:["a","b","c"]  

提示:

  • 0 <= digits.length <= 4
  • digits[i] 是范围 ['2', '9'] 的一个数字。

题目元素

给定[2,9]之间的数字和对应的字母,给定数字,返回从每个数字对应的字母数组中取一个能拼成的所有可能的字符串。

解题思路

又涉及到组合,所以可以采用回溯+递归的方式。 再回忆一下回溯的三要素:

  • 选择路径: 已经做出的选择;
  • 选择列表: 当前可以做的选择(还未选过的元素);
  • 终止条件: 达到决策数底层,无法再做选择。

先用map存储每个数字对应的字母数组作为基础数据。 这道题和77题的区别是,77题只给定了一个条件数组,这里可能会存在n个,然后每个条件数组中取一个,而不是一个数组中取n个。

首先,一个解答中只会存在n个字母,这n个分别来源于n个数字对应的字母数组中。所以新字符串中的字母来源都是固定的,即第一个字母来源于第一个数字,第二个字母来源于第二个数字。。。。。以此类推。 所以结合本题回溯的三要素

  • 选择路径path为每次选择的字母;
  • 每次的选择列表为该数字对应的字母数组;
  • 终止条件为当path.length=给定的数字长度。

类比推理

比如给定digits='23'。23对应的字母分别为'abc','def'; path=''; 第一次从'abc'中选择'a',然后递归调用选择第二个字母,从'def'中选择d,此时path='ad',长度等于digits.length,所以返回,进行回溯。撤销选择'd',选择'e',此时path='ae',又遇到了终止条件,所以返回,进行回溯。撤销选择'e',选择'f'......依次类推

代码实现

// 用来装所有数字对应字母的集合
static HashMap<Integer, List<String>> numLetterMap = new HashMap<>();

static {
    numLetterMap.put(2, Arrays.asList("a","b","c"));
    numLetterMap.put(3, Arrays.asList("d","e","f"));
    numLetterMap.put(4, Arrays.asList("g","h","i"));
    numLetterMap.put(5, Arrays.asList("j","k","l"));
    numLetterMap.put(6, Arrays.asList("m","n","o"));
    numLetterMap.put(7, Arrays.asList("p","q","r","s"));
    numLetterMap.put(8, Arrays.asList("t","u","v"));
    numLetterMap.put(9, Arrays.asList("w","x","y","z"));
}

/**
 *
 * @param digits
 * @return
 */
public static List<String> letterCombinations(String digits) {
    List<String> res = new ArrayList<>();
    if (digits == null || digits.length() == 0) {
        // 给定字符串为空直接返回
        return res;
    }
    char[] chars = digits.toCharArray();
    StringBuffer path = new StringBuffer();
    backTrack(chars,0,res,path);
    return res;
}

/**
 * 回溯获取路径
 *
 * @param chars 题目给定的所有数字
 * @param numIndex 数字下标
 * @param res 放所有路径的结果
 * @param path 当前遍历路径
 */
private static void backTrack(char[] chars,int numIndex, List<String> res, StringBuffer path) {
    // 中止条件,当前路径长度等于数字长度,则直接进行下一次回溯
    if (path.length() == chars.length) {
        // 遍历path,获取到新的字符串
        res.add(path.toString());
        return;
    }
    Integer number = Character.getNumericValue(chars[numIndex]);
    List<String> letters = numLetterMap.get(number);
    for (int i = 0; i < letters.size();i++) {
        // 做选择
        path.append(letters.get(i));
        backTrack(chars,numIndex+1,res,path);
        // 回退
        path.deleteCharAt(numIndex);
    }
}