回溯法与迭代法详解:如何从手机数字键盘生成字母组合

news2024/11/24 19:03:02

在这篇文章中,我们将详细介绍如何基于手机数字键盘的映射,给定一个仅包含数字 2-9 的字符串,输出它能够表示的所有字母组合。这是一个经典的回溯算法问题,适合初学者理解和掌握。

问题描述

给定一个数字字符串,比如 digits = "23",手机数字键盘的映射如下:
在这里插入图片描述

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

我们需要找到所有可能的字母组合。举个例子:

"23" -> ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]

17. 电话号码的字母组合 - 力扣(LeetCode)

1. 问题分析

从问题的角度看,这是一个组合问题。手机键盘上的每个数字对应一组字母,我们要找到给定数字的所有字母组合。关键在于如何构建这些组合。我们可以通过回溯法或者迭代法来解决。

1.1 回溯法的思路

回溯算法是递归算法的一种,适合用来枚举所有可能的结果。在这个问题中,回溯的关键点是从第一个数字开始,遍历所有可能的字母组合,逐步构建字符串,直到所有数字都被处理完毕。

具体步骤如下:

  1. 选择:我们从 digits 中每个数字开始,找到对应的字母集。
  2. 递归:对于字母集中的每个字母,我们递归地去构建字符串,直到遍历完所有的数字。
  3. 回退:在递归过程中,构建的字符串达到一定长度后,我们会回退(撤销上一步操作),尝试其他的可能性。
1.2 多种解法
  • 回溯法:利用递归去枚举所有可能的组合。复杂度为 O ( 3 n ) O(3^n) O(3n) O ( 4 n ) O(4^n) O(4n),取决于输入的数字个数。
  • 迭代法:使用队列的方式逐步构造出所有的字母组合。每处理一个数字,都会将当前已有的组合与对应的新字母结合。

接下来我们详细介绍这两种解法。


2. 解法一:回溯法(Backtracking)

2.1 解题思路

回溯算法的基本思路是递归地构建可能的组合,并在递归结束时撤销选择。具体来说,我们会从第一个数字开始,对应的字母集选择一个字母,然后递归处理下一个数字。

回溯的步骤:

  1. 如果已经构建的字符串长度等于 digits 的长度,我们就将它加入结果集。
  2. 否则,继续处理下一个数字。
  3. 每次递归返回时,撤销上一次的选择,尝试下一个字母。
2.2 代码实现
#include <iostream>
#include <vector>
#include <unordered_map>

using namespace std;

class Solution {
public:
    vector<string> ans;  // 存储结果
    string combination = "";  // 当前的字母组合

    void backtrack(const string& digits, unordered_map<char, string>& phone, int idx) {
        if (idx == digits.size()) {  // 当达到数字字符串的长度时
            ans.push_back(combination);  // 将组合加入结果集
            return;
        }
        
        char digit = digits[idx];  // 获取当前数字
        const string& letters = phone[digit];  // 获取当前数字对应的字母集
        
        for (char letter : letters) {  // 遍历字母集中的每个字母
            combination += letter;  // 做出选择
            backtrack(digits, phone, idx + 1);  // 递归处理下一个数字
            combination.pop_back();  // 回溯,撤销选择
        }
    }

    vector<string> letterCombinations(string digits) {
        if (digits.empty()) return {};  // 边界情况处理,空输入返回空数组
        
        // 建立数字到字母的映射
        unordered_map<char, string> phone = {
            {'2', "abc"}, {'3', "def"}, {'4', "ghi"}, {'5', "jkl"},
            {'6', "mno"}, {'7', "pqrs"}, {'8', "tuv"}, {'9', "wxyz"}
        };
        
        // 调用回溯函数
        backtrack(digits, phone, 0);
        
        return ans;  // 返回结果
    }
};
2.3 详细解释
  1. 递归终止条件:当索引 idx 达到 digits 的长度时,说明我们已经构造了一个完整的字母组合,将其加入结果集。
  2. 递归过程:对于当前数字,找到对应的字母集,遍历字母集中的每一个字母,加入到当前的组合字符串中,然后递归处理下一个数字。
  3. 回溯撤销选择:在递归返回后,我们调用 combination.pop_back() 撤销上一次递归中加入的字符,回退到上一个状态,从而尝试下一个字母。
2.4 示例模拟过程
  • 输入:digits = "23"
    • 递归过程:
      1. 处理第一个数字 2,对应的字母集是 ['a', 'b', 'c']
      2. 选中字母 'a',递归处理下一个数字 3,对应的字母集是 ['d', 'e', 'f']
      3. 选中字母 'd',完成组合 'ad',将其加入结果集。
      4. 回退到字母 'a',选择下一个字母 'e',构造组合 'ae',继续加入结果集。
      5. 继续这种方式,构造出 'af',然后回退选择字母 'b'
      6. 重复上述过程直到遍历所有字母,得到组合 ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]

3. 解法二:迭代法(使用队列)

3.1 解题思路

另一种方法是利用队列。我们可以使用一个队列来逐步生成组合:

  1. 从数字字符串的第一个数字开始,把对应的字母放入队列。
  2. 对于每一个数字,取出队列中所有的已有组合,然后在每个组合后附加当前数字对应的每一个字母,形成新的组合,再放回队列。
  3. 当处理完所有的数字时,队列中保存的就是所有可能的组合。
3.2 代码实现
#include <iostream>
#include <vector>
#include <unordered_map>
#include <queue>

using namespace std;

class Solution {
public:
    vector<string> letterCombinations(string digits) {
        if (digits.empty()) return {};  // 边界情况处理,空输入返回空数组
        
        // 建立数字到字母的映射
        unordered_map<char, string> phone = {
            {'2', "abc"}, {'3', "def"}, {'4', "ghi"}, {'5', "jkl"},
            {'6', "mno"}, {'7', "pqrs"}, {'8', "tuv"}, {'9', "wxyz"}
        };
        
        queue<string> q;  // 定义一个队列来保存组合
        q.push("");  // 先往队列中放入一个空字符串
        
        // 遍历输入数字字符串中的每个数字
        for (char digit : digits) {
            int n = q.size();  // 记录当前队列的大小
            const string& letters = phone[digit];  // 获取当前数字对应的字母集
            
            for (int i = 0; i < n; i++) {  // 对于当前队列中每个已有组合
                string current = q.front();  // 取出队首元素
                q.pop();  // 移除队首
                
                // 对当前数字的每个字母,将其加到已有组合的后面
                for (char letter : letters) {
                    q.push(current + letter);  // 将新组合放回队列
                }
            }
        }
        
        // 最终队列中的所有元素就是结果
        vector<string> ans;
        while (!q.empty()) {
            ans.push_back(q.front());
            q.pop();
        }
        
        return ans;
    }
};
3.3 示例讲解
  • 输入:digits = "23"
    1. 初始化队列:q = [""]
    2. 处理

第一个数字 2,对应的字母集 ['a', 'b', 'c']。将每个字母加到队列中的每一个已有组合(当前为空字符串 ""),得到队列 q = ["a", "b", "c"]
3. 处理第二个数字 3,对应的字母集 ['d', 'e', 'f']。将每个字母加到队列中的每一个已有组合,得到 q = ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]
4. 最终队列中的元素就是结果。


4. 总结

  • 回溯法:通过递归逐步构建可能的组合,并在递归结束后撤销选择,时间复杂度接近 O ( 3 n ) O(3^n) O(3n) O ( 4 n ) O(4^n) O(4n),取决于数字对应的字母数量。回溯算法的优点是易于实现,适合处理这种组合问题。

  • 迭代法(队列):通过队列逐步构建组合,时间复杂度也是 O ( 3 n ) O(3^n) O(3n) O ( 4 n ) O(4^n) O(4n)。虽然有时实现起来可能比递归稍微复杂,但它在一些情况下的性能表现会更好。

对于新手来说,回溯法可能是更直观的解法,因为它的思路清晰,非常适合处理类似的组合问题。

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

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

相关文章

vue3+FullCalendar+Element-plus修改的日程安排表

实现效果 安装Fullcalendar相关插件 npm install fullcalendar/core fullcalendar/daygrid fullcalendar/timegrid fullcalendar/list fullcalendar/interaction --save代码中使用到了时间转换和element-plus&#xff0c;安装dayjs和element-plus npm install element-plus e…

03 django管理系统 - 部门管理 - 部门列表

部门管理 首先我们需要在models里定义Dept类 # 创建部门表 class Dept(models.Model):name models.CharField(max_length100)head models.CharField(max_length100)phone models.CharField(max_length15)email models.EmailField()address models.CharField(max_length2…

MySql的binlog与数据的恢复

目录 什么是binlogbinlog的作用binlog的三种模式binlog的开启数据的恢复与回滚binlog日志的删除 什么是binlog binlog我们一般叫做归档日志&#xff0c;他是mysql服务器层的日志&#xff0c;跟存储引擎无关&#xff0c;他记录的是所有DDL和DML的语句&#xff0c;不包含查询语句…

文本语义检索系统的搭建过程,涵盖了召回、排序以及Milvus召回系统、短视频推荐等相关内容

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下本文详细介绍了文本语义检索系统的搭建过程&#xff0c;涵盖了召回、排序以及Milvus召回系统的相关内容。通过使用PyTorch框架&#xff0c;我们提供了样例代码&#xff0c;以帮助读者更好地理解和实践。该系统具有广…

基于STM32的车牌识别系统

基于STM32的车牌识别系统硬件设计 在智能交通系统中&#xff0c;车牌识别技术扮演着至关重要的角色。它不仅用于道路交通监控&#xff0c;还广泛应用于小区和停车场管理、收费站管理系统、车流统计以及移动车载系统等领域。本文将详细介绍基于STM32单片机的车牌识别系统的硬件…

Vue3 + TypeScript + Vite + Echarts

Vue3 TypeScript Vite Echarts 1、创建工程 npm create vitelatestcd echarts npm install npm run dev2、安装项目依赖模块 npm install types/node --save-devnpm install vue-router4npm install animate.css --save npm install gsap --savenpm install fetch --save …

2024年源代码加密软件推荐,十款超好用的源代码加密软件推荐

在当今数字化时代&#xff0c;源代码的安全性对于企业和开发者来说至关重要。无论是为了保护知识产权&#xff0c;还是为了防止恶意攻击&#xff0c;选择一款可靠的源代码加密软件都是必不可少的。本文将为您推荐2024年十款超好用的源代码加密软件&#xff0c;帮助您在保护代码…

10款超好用的电脑加密软件推荐|2024年常用电脑加密软件排行榜

随着数字化办公的普及&#xff0c;企业的数据安全面临前所未有的挑战。文件的泄露、窃取和丢失不仅会影响企业的商业利益&#xff0c;还可能导致客户隐私泄露&#xff0c;进而影响企业声誉。因此&#xff0c;选择一款合适的加密软件来保护公司机密文件变得尤为重要。2024年&…

AI产品经理怎么准备面试啊?

最近有些小伙伴&#xff0c;想要求职AI领域的产品经理&#xff0c;特别是AIGC的产品经理&#xff0c;但是不知道面试官会问哪些问题&#xff0c;也就不知道如何开始准备&#xff1f;该准备哪些东西&#xff1f;要准备到什么程度&#xff1f;最终导致迟迟不敢开始。 下面总共5家…

多级代理与提权维权

目录 代理构建FRP介绍下载配置⽂件&#xff1a; sock5代理Venom介绍下载配置 icmpsh介绍下载配置 pingtunnel介绍下载配置 EarthWorm介绍下载使用 权限提升win权限提升常⻅利⽤⼯具 Linux权限提升SUID提权 权限维持win权限维持系统服务后⻔⾃启动⽬录注册表后⻔其他类似隐藏⽤户…

西安国际数字影像产业园:文化创意产业的加速器

西安国际数字影像产业园作为文化创意产业的加速器&#xff0c;正为西安乃至全国的文化创意产业发展注入强大动力&#xff1a; 优越的基础条件&#xff1a;西安作为历史文化名城&#xff0c;文化底蕴深厚&#xff0c;为数字影像产业提供了丰富的创作素材和灵感源泉。西安国际数…

magic-html : 通用HTML数据提取器!DocAI:从非结构化文档中提取结构化数据!强大、快速、开源的微信机器人底层框架:wcf.js!

magic-html : 通用HTML数据提取器&#xff01;DocAI&#xff1a;从非结构化文档中提取结构化数据&#xff01;强大、快速、开源的微信机器人底层框架&#xff1a;wcf.js&#xff01; magic-html : 通用HTML数据提取器 magic-html提供了一套工具&#xff0c;能够轻松地从HTML中…

闪电麦昆 语音控制齿轮行进轨迹,ESP32搭配语音控制板,串口通信,附视频演示地址

演示地址 https://www.bilibili.com/video/BV1cW421d79L/?vd_sourceb8515e53f6d4c564b541d98dcc9df990 语音控制板的配置 web展示页面 esp32 程序 #include <ESP8266WiFi.h> #include <ESP8266WebServer.h> #include <LittleFS.h> #include <WebSo…

最全方案解决Android Studio中使用lombok插件错误: 找不到符号的问题

直接原因 先直接说原因&#xff0c;小部分是因为配置错误导致的&#xff0c;注意查看下面的步骤即可&#xff0c;另一大部分是因为Java和Kotlin混编的问题&#xff0c;lombok和kapt冲突&#xff0c;其实你用了kotlin基本不需要用lombok&#xff0c;多此一举&#xff01;所以可…

最新版 Global Mapper 26 发布

我们在《工作中常用的软件&#xff0c;可直接下载0.3m卫星影像、DEM和土地覆盖数据》一文中&#xff0c;为你分享了GlobalMapper25版本。 现在&#xff0c;该神器的最新版GlobalMapper26已发布&#xff0c;如果这两个版本的软件你都需要&#xff0c;请在文末查看它们的下载方法…

Spring源码分析:bean加载流程

背景 在Spring中&#xff0c;Bean的加载和管理是其核心功能之一&#xff0c;包括配置元数据解析、Bean定义注册、实例化、属性填充、初始化、后置处理器处理、完成创建和销毁等步骤。 源码入口 AbstractBeanFactory#doGetBean 具体源码流程如下&#xff1a; bean加载流程&#…

怎么利用商品详情API接口实现数据获取与应用?

在当今数字化的商业时代&#xff0c;高效获取和利用商品数据对于企业和开发者来说至关重要。商品详情 API 接口为我们提供了一种便捷的方式来获取丰富的商品信息&#xff0c;从而实现各种有价值的应用。本文将深入探讨如何利用商品详情 API 接口实现数据获取与应用。 一、商品…

信号转导的风暴中心:ERK1/2

前 言 ERK1/2是RAF-MEK-ERK信号通路的关键组成部分&#xff0c;在Thr202、Tyr204位点被磷酸化从而激活&#xff0c;进而激活多种与细胞增殖、分化、迁移和血管生成相关的底物&#xff08;超过160种&#xff09;。因此ERK1/2的(Thr202, Tyr204)/(Thr185, Tyr187)磷酸化是ERK激…

从SQL Server过渡到PostgreSQL:理解模式的差异

前言 随着越来越多的企业转向开源技术&#xff0c;商业数据库管理员和开发者也逐渐面临向PostgreSQL迁移的需求。 虽然SQL Server和PostgreSQL共享许多数据库管理系统&#xff08;RDBMS&#xff09;的基本概念&#xff0c;但它们在处理某些结构上的差异可能会让人感到困惑&…

利用Spring Boot实现医疗病历的B2B平台集成

第5章 系统实现 5.1 管理员角色 5.1.1 医院管理 管理员可以在医院管理界面对医院信息进行添加&#xff0c;修改&#xff0c;删除&#xff0c;查询操作。医院管理页面的运行结果如图5-1所示&#xff1a; 图5-1医院管理界面 5.1.2 医院注册 管理员可以在医院注册界面对医院信息…