leetcode17.电话号码的字母组合:字符串映射与回溯的巧妙联动

news2025/6/3 4:58:26

一、题目深度解析与字符映射逻辑

题目描述

给定一个仅包含数字 2-9 的字符串 digits,返回所有它能表示的字母组合。数字与字母的映射关系如下(与电话按键相同):

2: "abc", 3: "def", 4: "ghi", 5: "jkl", 
6: "mno", 7: "pqrs", 8: "tuv", 9: "wxyz"

例如,输入 digits = "23",应返回 ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]。题目要求:

  • 输出的字母组合需包含所有可能情况
  • 每个数字对应字母的不同组合都要考虑
  • 结果顺序不做要求

核心难点剖析

本题的关键在于如何根据数字字符串的每个字符,找到对应的字母集合,并通过回溯算法生成所有可能的组合。其中,数字与字母的映射关系需要通过数据结构记录,而回溯过程中的状态管理则是确保组合完整性的核心。

二、递归回溯解法的核心实现与逻辑框架

class Solution {
    // 数字到字母的映射数组,0和1无对应字母,用空字符串占位
    String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    List<String> res = new ArrayList<>();  // 存储所有结果组合
    StringBuilder sb = new StringBuilder();  // 记录当前组合

    public List<String> letterCombinations(String digits) {
        if (digits.length() == 0) {
            return res;  // 处理空字符串输入
        }
        backtracking(digits, 0);  // 从第一个数字开始回溯
        return res;  // 返回所有组合
    }

    public void backtracking(String digits, int num) {
        if (num == digits.length()) {
            res.add(sb.toString());  // 保存当前组合
            return;
        }
        // 获取当前数字对应的字母字符串
        String str = numString[digits.charAt(num) - '0'];
        for (int i = 0; i < str.length(); i++) {
            sb.append(str.charAt(i));  // 选择当前字母
            backtracking(digits, num + 1);  // 递归处理下一个数字
            sb.deleteCharAt(sb.length() - 1);  // 回溯:撤销当前选择
        }
    }
}

核心设计解析

  1. 字符映射数组
    • numString数组建立数字与字母的映射关系,通过numString[digits.charAt(num) - '0']可快速获取对应字母集合。其中,- '0'的操作将字符类型的数字转换为数组索引所需的整数类型。
  2. 结果与状态变量
    • res:使用ArrayList存储所有符合要求的字母组合。
    • sbStringBuilder用于高效构建当前组合,避免频繁字符串拼接带来的性能损耗。
  3. 递归终止条件
    • if (num == digits.length()):当处理完数字字符串的所有字符时,将当前sb记录的组合加入res,并结束当前递归分支。
  4. 回溯核心逻辑
    • 遍历当前数字对应的字母字符串,每次选择一个字母加入sb
    • 递归调用backtracking处理下一个数字。
    • 递归返回后,通过sb.deleteCharAt(sb.length() - 1)删除最后一个字母,回溯到上一状态,尝试其他可能的组合。

三、核心问题解析:字符串映射与回溯联动

1. 数字字符到字母集合的映射实现

numString数组是连接数字与字母的桥梁。以输入字符串"23"为例:

  • 处理第一个数字'2'时,numString[2 - '0']获取到"abc",即数字2对应的字母集合。
  • 处理第二个数字'3'时,numString[3 - '0']获取到"def"

这种映射方式通过数组下标直接定位,时间复杂度为O(1),相比使用Map等数据结构更加简洁高效。

2. 回溯算法的具体执行逻辑

backtracking方法中,回溯过程通过递归与状态回退实现:

  • 选择阶段
    sb.append(str.charAt(i));
    backtracking(digits, num + 1);
    
    每次从当前数字对应的字母集合中选择一个字母加入sb,并递归处理下一个数字,深入探索当前选择下的所有可能组合。
  • 回退阶段
    sb.deleteCharAt(sb.length() - 1);
    
    递归返回后,删除sb中最后一个字母,撤销当前选择,以便尝试该数字对应的其他字母,确保所有组合都能被枚举到。

3. 递归过程中的状态传递

在递归调用过程中,num参数记录当前处理的数字位置,sb则记录当前构建的组合状态。每一层递归都基于上一层的状态继续构建,确保组合的完整性和准确性。例如:

  • 当处理数字字符串"23"时,第一层递归处理数字2,第二层递归处理数字3。在第二层递归中,sb已包含第一层递归选择的字母(如'a'),在此基础上继续添加数字3对应的字母(如'd'),最终形成组合"ad"

四、递归流程深度模拟:以输入"23"为例

  1. 初始调用backtracking("23", 0)
    • num = 0,处理第一个数字'2'str = "abc"
  2. 选择字母'a'
    • sb.append('a'),此时sb = "a"
    • 递归调用backtracking("23", 1),处理第二个数字'3'str = "def"
    • 选择字母'd'sb.append('d'),此时sb = "ad",满足终止条件,将"ad"加入res
    • 回溯:sb.deleteCharAt(sb.length() - 1)sb变回"a",继续选择字母'e',形成"ae"并加入res,以此类推,完成数字3对应字母的所有组合。
  3. 回到数字2的处理
    • 撤销字母'a'的选择,sb.deleteCharAt(sb.length() - 1)sb清空。
    • 选择字母'b',重复上述递归过程,生成以'b'开头的所有组合(如"bd""be""bf")。
  4. 选择字母'c'
    • 同样进行递归与回溯,生成以'c'开头的所有组合(如"cd""ce""cf")。
  5. 最终结果
    res包含["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"],涵盖所有可能的字母组合。

五、算法复杂度分析

1. 时间复杂度

假设数字字符串digits的长度为n,每个数字平均对应m个字母(实际最多4个)。

  • 对于每个数字,都需要遍历其对应的字母集合,因此时间复杂度为 O ( m n ) O(m^n) O(mn) 。因为每个数字位置都有m种选择,总共n个位置,属于指数级复杂度。

2. 空间复杂度

  • 递归栈的深度最大为n,因此空间复杂度为 O ( n ) O(n) O(n)
  • res存储所有结果组合,最坏情况下,组合数量为 m n m^n mn,每个组合长度为n,因此res占用空间为 O ( n × m n ) O(n \times m^n) O(n×mn) 。整体空间复杂度主要由res决定,为 O ( n × m n ) O(n \times m^n) O(n×mn)

六、核心技术点总结:字母组合生成的关键要素

  1. 字符映射设计:通过数组建立数字与字母的高效映射,简化查找过程。
  2. 回溯状态管理:利用StringBuilder动态构建组合,并通过递归与回退操作确保枚举所有可能组合。
  3. 递归终止条件:根据数字字符串的处理进度判断是否完成一个组合的构建,及时保存结果。

七、常见误区与优化建议

1. 字符转换错误

  • 误区:遗漏- '0'操作,导致无法正确获取字母集合。
    String str = numString[digits.charAt(num)]; // 错误,未转换字符为数字
    
  • 正确做法:使用numString[digits.charAt(num) - '0']将字符类型数字转换为数组索引。

2. 回溯状态未完全回退

  • 误区:在递归返回后,未删除sb中的最后一个字母,导致后续组合错误。
    sb.append(str.charAt(i));
    backtracking(digits, num + 1);
    // 缺少 sb.deleteCharAt(sb.length() - 1);
    
  • 正确做法:确保在每次递归返回后,通过sb.deleteCharAt(sb.length() - 1)撤销当前选择。

3. 优化建议:迭代实现

若担心递归深度过深导致栈溢出,可以考虑使用迭代方式,借助队列或栈模拟递归过程。例如,使用队列存储中间状态,每次从队列中取出一个状态,扩展下一个数字对应的字母组合并重新入队,直至处理完所有数字。这种方式可以将空间复杂度优化为 O ( m n ) O(m^n) O(mn) ,避免递归栈的潜在风险。

通过对本题递归回溯解法的详细剖析,我们深入理解了如何利用字符串映射和回溯算法,高效生成电话号码对应的字母组合。这种解题思路在处理组合枚举类问题时具有广泛的应用价值,能够帮助我们解决更多类似的算法问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2395010.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Gartner《2025 年软件工程规划指南》报告学习心得

一、引言 软件工程领域正面临着前所未有的变革与挑战。随着生成式人工智能(GenAI)等新兴技术的涌现、市场环境的剧烈动荡以及企业对软件工程效能的更高追求,软件工程师们必须不断适应和拥抱变化,以提升自身竞争力并推动业务发展。Gartner 公司发布的《2025 年软件工程规划…

Java Class类文件结构

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

quasar electron mode如何打包无边框桌面应用程序

预览 开源项目Tokei Kun 一款简洁的周年纪念app&#xff0c;现已发布APK&#xff08;安卓&#xff09;和 EXE&#xff08;Windows&#xff09; 项目仓库地址&#xff1a;Github Repo 应用下载链接&#xff1a;Github Releases Preparation for Electron quasar dev -m elect…

【HW系列】—Windows日志与Linux日志分析

文章目录 一、Windows日志1. Windows事件日志2. 核心日志类型3. 事件日志分析实战详细分析步骤 二、Linux日志1. 常见日志文件2. 关键日志解析3. 登录爆破检测方法日志分析核心要点 一、Windows日志 1. Windows事件日志 介绍&#xff1a;记录系统、应用程序及安全事件&#x…

VIN码识别解析接口如何用C#进行调用?

一、什么是VIN码识别解析接口&#xff1f; VIN码不仅是车辆的“身份证”&#xff0c;更是连接制造、销售、维修、保险、金融等多个环节的数字纽带。而VIN码查询API&#xff0c;正是打通这一链条的关键工具。 无论是汽车电商平台、二手车商、维修厂&#xff0c;还是保险公司、金…

动态规划之网格图模型(一)

文章目录 动态规划之网格图模型&#xff08;一&#xff09;LeetCode 64. 最小路径和思路Golang 代码 LeetCode 62. 不同路径思路Golang 代码 LeetCode 63. 不同路径 II思路Golang 代码 LeetCode 120. 三角形最小路径和思路Golang 代码 LeetCode 3393. 统计异或值为给定值的路径…

PCB设计实践(三十)地平面完整性

在高速数字电路和混合信号系统设计中&#xff0c;地平面完整性是决定PCB性能的核心要素之一。本文将从电磁场理论、信号完整性、电源分配系统等多个维度深入剖析地平面设计的关键要点&#xff0c;并提出系统性解决方案。 一、地平面完整性的电磁理论基础 电流回流路径分析 在PC…

使用ray扩展python应用之流式处理应用

流式处理就是数据一来&#xff0c;咱们就得赶紧处理&#xff0c;不能攒批再算。这里的实时不是指瞬间完成&#xff0c;而是要在数据产生的那一刻&#xff0c;或者非常接近那个时间点&#xff0c;就做出响应。这种处理方式&#xff0c;我们称之为流式处理。 流式处理的应用场景…

IP证书的作用与申请全解析:从安全验证到部署实践

在网络安全领域&#xff0c;IP证书&#xff08;IP SSL证书&#xff09;作为传统域名SSL证书的补充方案&#xff0c;专为公网IP地址提供HTTPS加密与身份验证服务。本文将从技术原理、应用场景、申请流程及部署要点四个维度&#xff0c;系统解析IP证书的核心价值与操作指南。 一…

【Linux系列】Linux/Unix 系统中的 CPU 使用率

博客目录 多核处理器时代的 CPU 使用率计算为什么要这样设计&#xff1f; 解读实际案例&#xff1a;268.76%的 CPU 使用率性能分析的意义 相关工具与监控实践1. top 命令2. htop 命令3. mpstat 命令4. sar 命令 实际应用场景容量规划性能调优故障诊断 深入理解&#xff1a;CPU …

C++语法系列之模板进阶

前言 本次会介绍一下非类型模板参数、模板的特化(特例化)和模板的可变参数&#xff0c;不是最开始学的模板 一、非类型模板参数 字面意思,比如&#xff1a; template<size_t N 10> 或者 template<class T,size_t N 10>比如&#xff1a;静态栈就可以用到&#…

基于图神经网络的自然语言处理:融合LangGraph与大型概念模型的情感分析实践

在企业数字化转型进程中&#xff0c;非结构化文本数据的处理与分析已成为核心技术挑战。传统自然语言处理方法在处理客户反馈、社交媒体内容和内部文档等复杂数据集时&#xff0c;往往难以有效捕获文本间的深层语义关联和结构化关系。大型概念模型&#xff08;Large Concept Mo…

R 语言科研绘图 --- 热力图-汇总

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…

解决访问网站提示“405 很抱歉,由于您访问的URL有可能对网站造成安全威胁,您的访问被阻断”问题

一、问题描述 本来前几天都可以正常访问的网站&#xff0c;但是今天当我们访问网站的时候会显示“405 很抱歉&#xff0c;由于您访问的URL有可能对网站造成安全威胁&#xff0c;您的访问被阻断。您的请求ID是&#xff1a;XXXX”&#xff0c;而不能正常的访问网站&#xff0c;如…

机器学习中的关键术语及其含义

神经元及神经网络 机器学习中的神经网络是一种模仿生物神经网络的结构和功能的数学模型或计算模型。它是指按照一定的规则将多个神经元连接起来的网络。 神经网络是一种运算模型&#xff0c;由大量的节点&#xff08;或称神经元&#xff09;之间相互联接构成。每个节点代表一…

网络编程1_网络编程引入

为什么需要网络编程&#xff1f; 用户再在浏览器中&#xff0c;打开在线视频资源等等&#xff0c;实质上说通过网络&#xff0c;获取到从网络上传输过来的一个资源。 与打开本地的文件类似&#xff0c;只是这个文件的来源是网络。相比本地资源来说&#xff0c;网络提供了更为…

【Day38】

DAY 38 Dataset和Dataloader类 对应5. 27作业 知识点回顾&#xff1a; Dataset类的__getitem__和__len__方法&#xff08;本质是python的特殊方法&#xff09;Dataloader类minist手写数据集的了解 作业&#xff1a;了解下cifar数据集&#xff0c;尝试获取其中一张图片 import …

HTML Day04

Day04 0.引言1. HTML字符实体2. HTML表单2.1 表单标签2.2 表单示例 3. HTML框架4. HTML颜色4.1 16进制表示法4.2 rgba表示法4.3 名称表达法 5. HTML脚本 0.引言 刚刚回顾了前面几篇博客&#xff0c;感觉写的内容倒是很详细&#xff0c;每个知识点都做了说明。但是感觉在知识组织…

云原生安全基石:Kubernetes 核心概念与安全实践指南

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. Kubernetes 架构全景 Kubernetes&#xff08;简称 K8s&#xff09;采用主从架构&#xff0c;由控制平面&#xff08;Control Plane&…

autodl 安装了多个conda虚拟环境 选择合适虚拟环境的语句

1.conda env list 列出所有虚拟环境 可以看到&#xff0c;我有两个虚拟环境&#xff0c;一个是joygen&#xff0c;一个是base conda activate base 或者 conda activate joygen 激活对应的环境。我选择激活 joygen 环境 然后就可以在joygen环境下进行操作了 base环境也是同理…