墨风如雪博客

  • 源码小店
  • 导航站
  • 登录
  • java
  • 资源分享
让AI使用变得如此简单
  1. 首页
  2. 算法
  3. 正文

每日一道算法题:电话号码的字母组合算法实现

2023年 7月 21日 77点热度 0人点赞 0条评论

算法概述

本算法用于求解给定电话号码的所有可能的字母组合。电话号码由数字构成,每个数字分别对应一些字母,我们需要找出所有可能的字母组合。

电话号码所表示的字母

由于电话号码由数字构成,每个数字分别对应一些字母,因此我们需要先构建数字与字母的映射关系。

以下是数字与字母的对应关系:

数字 字母
2 a b c
3 d e f
4 g h i
5 j k l
6 m n o
7 p q r s
8 t u v
9 w x y z

算法实现过程

  1. 输入电话号码
  2. 构建数字与字母的映射关系
  3. 深度优先搜索找出所有的组合

其中,第3步需要递归实现。

具体实现细节将在接下来的代码实现中详细阐述。

代码实现

输入电话号码的函数

private static String getInput() {
    Scanner sc = new Scanner(System.in);
    String input;
    do {
        System.out.print("请输入电话号码(仅包含2-9范围内的数字):");
        input = sc.nextLine();
    } while(!isInputValid(input));
    return input;
}

此函数用于读取用户输入的电话号码。

构建数字与字母的映射关系的函数

private static void buildMap(Map<Character, List<Character>> map) {
    map.put('2', Arrays.asList('a', 'b', 'c'));
    map.put('3', Arrays.asList('d', 'e', 'f'));
    map.put('4', Arrays.asList('g', 'h', 'i'));
    map.put('5', Arrays.asList('j', 'k', 'l'));
    map.put('6', Arrays.asList('m', 'n', 'o'));
    map.put('7', Arrays.asList('p', 'q', 'r', 's'));
    map.put('8', Arrays.asList('t', 'u', 'v'));
    map.put('9', Arrays.asList('w', 'x', 'y', 'z'));
}

此函数用于构建数字与字母的映射关系。

深度优先搜索的函数

private static void dfs(Map<Character, List<Character>> map, String digits, String combination, List<String> combinations) {
    if(digits.length() == 0) { // 递归边界
        combinations.add(combination); // 加入结果集
    }else {
        Character digit = digits.charAt(0); // 取出第一个数字
        List<Character> letters = map.get(digit); // 取出该数字对应的所有字母
        for(Character letter:letters) { // 依次尝试每个字母
            dfs(map, digits.substring(1), combination + letter, combinations); // 递归下一层
        }
    }
}

此函数用于深度优先搜索,调用函数时,需传入以下参数:

  • Map<Character, List<Character>> map:数字与字母的映射关系。
  • String digits:待处理的电话号码。
  • String combination:已生成的字母组合。
  • List<String> combinations:存放所有可能的字母组合。

主函数

public static void main(String[] args) {
    String digits = getInput(); // 输入电话号码
    Map&lt;Character, List&lt;Character&gt;&gt; map = new HashMap&lt;&gt;();
    buildMap(map); // 构建数字与字母的映射关系
    List&lt;String&gt; combinations = new ArrayList&lt;&gt;();
    dfs(map, digits, &quot;&quot;, combinations); // 深度优先搜索
    System.out.println(&quot;电话号码&quot; + digits + &quot;对应的所有可能的字母组合为:&quot; + combinations);
}

此函数为程序入口,调用了前面提到的3个函数,找出所有可能的字母组合,并输出。

算法测试

测试1:

输入:23

输出:电话号码23对应的所有可能的字母组合为:[ad, ae, af, bd, be, bf, cd, ce, cf]

测试2:

输入:79

输出:电话号码79对应的所有可能的字母组合为:[pw, px, py, pz, qw, qx, qy, qz, rw, rx, ry, rz, sw, sx, sy, sz]

算法优化

剪枝优化

由于存在多个数字对应同一组字母,因此可以在搜索的时候增加一个剪枝操作,以减少无用的搜索。

private static void dfs(Map&lt;Character, List&lt;Character&gt;&gt; map, String digits, String combination, List&lt;String&gt; combinations) {
    if(digits.length() == 0) { // 递归边界
        combinations.add(combination); // 加入结果集
    }else {
        Character digit = digits.charAt(0); // 取出第一个数字
        List&lt;Character&gt; letters = map.get(digit); // 取出该数字对应的所有字母
        for(Character letter:letters) { // 依次尝试每个字母
            dfs(map, digits.substring(1), combination + letter, combinations); // 递归下一层
        }
    }
}

可以在循环体中加入以下判断:

if(combinations.size() &gt;= 5000) { // 如果结果集数量超过5000,则退出搜索
    break;
}

当结果集数量超过5000时,直接退出搜索,避免无用的搜索。

数组优化

由于数字对应的字母都是固定的,可以考虑将字母表构造成一个String类型的数组。

private static void dfs(String[] map, String digits, String combination, List&lt;String&gt; combinations) {
    if(digits.length() == 0) { // 递归边界
        combinations.add(combination); // 加入结果集
    }else {
        char digit = digits.charAt(0); // 取出第一个数字
        String letters = map[digit - &#039;2&#039;]; // 取出该数字对应的所有字母
        for(int i = 0; i &lt; letters.length(); i++) { // 依次尝试每个字母
            dfs(map, digits.substring(1), combination + letters.charAt(i), combinations); // 递归下一层
        }
    }
}

由于字符的ASCII码是连续的,因此可以通过计算字符的差值来获取数字。

同样,建立数字和字母表的映射关系可以改为:

private static void buildMap(String[] map) {
    map[0] = &quot;abc&quot;;
    map[1] = &quot;def&quot;;
    map[2] = &quot;ghi&quot;;
    map[3] = &quot;jkl&quot;;
    map[4] = &quot;mno&quot;;
    map[5] = &quot;pqrs&quot;;
    map[6] = &quot;tuv&quot;;
    map[7] = &quot;wxyz&quot;;
}

并在主函数中进行调用:

public static void main(String[] args) {
    String digits = getInput();
    String[] map = new String[8];
    buildMap(map);
    List&lt;String&gt; combinations = new ArrayList&lt;&gt;();
    dfs(map, digits, &quot;&quot;, combinations);
    System.out.println(&quot;电话号码&quot; + digits + &quot;对应的所有可能的字母组合为:&quot; + combinations);
}

对于小数据量的测试用例来说,这样的优化效果可能并不明显,但在数据量大的情况下,优化后的代码可以更大程度上地提升程序的性能。

扩展问题

输入多个电话号码,求它们所有字母组合的并集

思路:将每个号码求出对应的所有字母组合,并存储到一个集合中。最后将所有集合取并集即可。

public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.print(&quot;请输入电话号码数量:&quot;);
    int n = sc.nextInt(); sc.nextLine();
    String[] digits = new String[n];
    for(int i = 0; i &lt; n; i++) {
        digits[i] = getInput();
    }
    String[] map = new String[8];
    buildMap(map);
    Set&lt;String&gt; set = new HashSet&lt;&gt;();
    for(int i = 0; i &lt; n; i++) {
        List&lt;String&gt; combinations = new ArrayList&lt;&gt;();
        dfs(map, digits[i], &quot;&quot;, combinations);
        set.addAll(combinations);
    }
    System.out.println(&quot;所有电话号码对应的所有可能的字母组合为:&quot; + set);
}

输入一个字符串和一个电话号码,求字符串中是否包含电话号码所表示的所有字母组合

思路:先将电话号码对应的所有字母组合求出来,再依次判断字符串中是否包含该组合即可。

public static void main(String[] args) {
    String digits = getInput();
    String[] map = new String[8];
    buildMap(map);
    List&lt;String&gt; combinations = new ArrayList&lt;&gt;();
    dfs(map, digits, &quot;&quot;, combinations);
    System.out.println(&quot;电话号码&quot; + digits + &quot;对应的所有可能的字母组合为:&quot; + combinations);

    Scanner sc = new Scanner(System.in);
    System.out.print(&quot;请输入字符串:&quot;);
    String str = sc.nextLine();
    boolean flag = true;
    for(String combination:combinations) {
        if(!str.contains(combination)) {
            flag = false;
            break;
        }
    }
    if(flag) {
        System.out.println(&quot;字符串中包含电话号码对应的所有字母组合&quot;);
    }else {
        System.out.println(&quot;字符串中不包含电话号码对应的所有字母组合&quot;);
    }
}

需要注意的是,输入的字符串和电话号码都要进行非空和合法性验证。

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: java 动态规划 数组 算法
最后更新:2025年 3月 24日

墨风如雪

一个热爱生活,热爱分享的程序员

打赏 点赞
< 上一篇
下一篇 >

文章评论

您需要 登录 之后才可以评论

墨风如雪

一个热爱生活,热爱分享的程序员

最新 热点 随机
最新 热点 随机
小红书AI新里程碑:dots.llm1,中文MoE的“人文”突破! 告别机械感!OpenAudio S1让AI声音活起来 Sora触手可及!微软必应AI视频生成器,全民创作时代来临? 阿里WebAgent开源:引领自主搜索新纪元 重磅炸弹!字节跳动开源BAGEL:70亿参数,统一多模态理解与生成,AI“全能王”诞生记! 小米MiMo-VL:7B参数,怎么就成了多模态界的“越级打怪王”?
AI圈炸锅了!Mistral Medium 3:性能 SOTA,成本打骨折,企业玩家的新宠?字节终于开源“扣子”同款引擎了!FlowGram:AI 时代的可视化工作流利器告别“微信黑箱”!Chatlog:让你的聊天记录也能拥有“AI大脑”!字节跳动 Seed-Coder-8B:不靠人工洗数据,这80亿参数的小模型如何写出顶尖代码?85倍速的视觉革命:苹果发布 FastVLM,让你的 iPhone ‘看图说话’,快到飞起!告别AI视频“变脸怪”!腾讯混元Hunyuan Custom重磅开源,主体一致性“王炸”来了!
开源新王者DeepSeek-V3-0324:代码能力叫板Claude 3.7,MIT协议引爆AI普惠革命 全网最毒舌的AI暴走指南!一秒教你嘴炮封神! iOS快捷指令×DeepSeek:三步打造智能自动化工作流 图像生成新篇章:OpenAI GPT-image-1 模型深度解析与应用前瞻 炸裂!OpenAI 不声不响发布 GPT-4.1 全家桶,开发者狂喜:更快、更强、还更便宜? 如何使用Java原子类实现自旋锁和读写锁?
标签聚合
AI java 设计模式 spring deepseek 教程 算法 动态规划

COPYRIGHT © 2023 墨风如雪博客. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

免责声明 - 隐私政策