《剑指 Offer》专项突破版 - 面试题 108 : 单词演变(C++ 实现)

目录

前言

单向广度优先搜索

双向广度优先搜索


 


前言

题目链接:单词演变

题目

输入两个长度相同但内容不同的单词(beginWord 和 endWord)和一个单词列表(wordList),求从 beginWord 到 endWord 的演变序列的最短长度,要求每步只能改变单词中的一个字母,并且演变过程中每步得到的单词都必须在给定的单词列表中。如果不能从 beginWord 演变到 endWord,则返回 0。假设所有单词只包含英文小写字母。

例如,如果 beginWord 为 "hit",endWord 为 "cog",wordList 为 ["hot", "dot", "dog", "lot", "log", "cog"],则演变序列的最短长度为 5,一个可行的演变序列为 "hit"->"hot"->"dot"->"dog"->"cog"。

分析

应用图相关算法的前提是找出图中的节点和边。这个问题是关于单词的演变的,所以每个单词就是图中的一个节点。如果两个单词能够相互演变(改变一个单词的一个字母就能变成另一个单词),那么这两个单词之间有一条边相连。例如,可以用下图表示 "hit"、"hot"、"dot"、"dog"、"lot"、"log"、"cog" 的演变关系,可以看出,从 "hit" 演变成 "cog" 的最短序列的长度为 5,一个可行的最短序列经过的节点用阴影表示。


单向广度优先搜索

这个题目要求计算最短演变序列的长度,即求图中两个节点的最短距离。表示单词演变的图也是一个无权图,按照题目的要求,图中两个节点的距离是连通两个节点的路径经过的节点的数目。通常用广度优先搜索计算无权图中的最短路径,广度优先搜索通常需要用到队列。

为了求得两个节点之间的最短距离,常见的解法是用两个队列实现广度优先搜索算法。一个队列 q1 中存放离起始节点距离为 d 的节点,当从这个队列中取出节点并访问的时候,与队列 q1 中相邻的节点离起始节点的距离都是 d + 1,将这些相邻的节点存放到另一个队列 q2 中。当队列 q1 中的所有节点都访问完毕时,再访问队列 q2 中的节点,并将相邻的节点放入 q1 中。可以交替使用 q1 和 q2 这两个队列由近及远地从起始节点开始搜索所有节点

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        unordered_set<string> notVisited(wordList.begin(), wordList.end());
        queue<string> q1, q2;
        q1.push(beginWord);
        int length = 1;
        while (!q1.empty())
        {
            string word = q1.front();
            q1.pop();
            if (word == endWord)
                return length;
            
            vector<string> neighbors = getNeighbors(word);
            for (string& neighbor : neighbors)
            {
                if (notVisited.count(neighbor))
                {
                    q2.push(neighbor);
                    notVisited.erase(neighbor);
                }
            }
​
            if (q1.empty())
            {
                q1 = q2;
                q2 = queue<string>();
                ++length;
            }
        }
        return 0;
    }
private:
    vector<string> getNeighbors(string& word) {
        vector<string> neighbors;
        for (int i = 0; i < word.size(); ++i)
        {
            char old = word[i];
            for (char ch = 'a'; ch <= 'z'; ++ch)
            {
                if (ch != old)
                {
                    word[i] = ch;
                    neighbors.push_back(word);
                }
            }
            word[i] = old;
        }
        return neighbors;
    }
};

上述代码首先将起始节点 beginWord 添加到队列 q1 中。接下来每一步从队列 q1 中取出一个节点 word 访问。如果 word 就是目标节点,则搜索结束;否则找出所有与 word 相邻的节点并将相邻的节点放到队列 q2 中。当队列 q1 中的所有节点都访问完毕时交换队列 q1 和 q2,以便接下来访问原来存放在队列 q2 中的节点。每次交换队列 q1 和 q2 都意味着距离初始结点距离为 d 的节点都访问完毕,接下来将访问距离为 d + 1 的节点,因此距离值增加 1

上述代码将单词列表中还没有访问过的节点放入 notVisited 中,每当一个单词被访问过就从这个 unordered_set 中删除。如果一个节点不在 notVisited 中,要么它不在单词列表之中,要么之前已经访问过,不管是哪种情况这个节点都可以忽略


双向广度优先搜索

这个题目是关于单一起始节点、单一目标节点的最短距离问题。前面的解法是从起始节点出发不断朝着目标节点的方向搜索,直到到达目标节点。针对这类问题有一种常用的优化方法,即在从起始节点出发不断朝着目标节点的方向搜索的同时,也从目标节点出发不断朝着起始节点的方向搜索。这种双向搜索的方法能够缩小搜索空间,从而提高搜索效率

下图是双向广度优先搜索缩小搜索空间的示意图。假设目标是求出图中顶部的黑色节点到底部的黑色节点的最短距离。如果采用广度优先搜索,那么图中所有节点都可能会被搜索到,如下图 (a) 所示。如果采用双向广度优先搜索,则分别从起始节点和目标节点出发不断搜索,直到在中间某个位置相遇,那么图中只有部分节点被搜索到,如下图 (b) 所示。

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        unordered_set<string> notVisited(wordList.begin(), wordList.end());
        if (notVisited.count(endWord) == 0)
            return 0;
        
        unordered_set<string> s1, s2;
        s1.insert(beginWord), s2.insert(endWord);
        int length = 2;
        while (!s1.empty() && !s2.empty())
        {
            if (s2.size() < s1.size())
                s1.swap(s2);
            
            unordered_set<string> s3;
            for (const string& word : s1)
            {
                vector<string> neighbors = getNeighbors(word);
                for (string& neighbor : neighbors)
                {
                    if (s2.count(neighbor))
                        return length;
                    
                    if (notVisited.count(neighbor))
                    {
                        s3.insert(neighbor);
                        notVisited.erase(neighbor);
                    }
                }
            }
            ++length;
            s1 = s3;
        }
        return 0;
    }
private:
    vector<string> getNeighbors(const string& word) {
        string tmp = word;
        vector<string> neighbors;
        for (int i = 0; i < tmp.size(); ++i)
        {
            char old = tmp[i];
            for (char ch = 'a'; ch <= 'z'; ++ch)
            {
                if (ch != old)
                {
                    tmp[i] = ch;
                    neighbors.push_back(tmp);
                }
            }
            tmp[i] = old;
        }
        return neighbors;
    }
};

在上述代码中,s1 和 s2 分别存放两个方向上当前需要访问的节点,s3 用来存放与当前访问的节点相邻的节点。之所以这里用的是 unordered_set 而不是 queue,是因为需要判断从一个方向搜索到的节点在另一个方向是否已经访问过。只需要 O(1) 的时间就能判断 unordered_set 中是否包含一个元素

先将起始节点 beginWord 添加到 s1 中,将目标节点 endWord 添加到 s2 中。接下来每次 while 循环都是从需要访问的节点数目少的方向搜索,这样做是为了缩小搜索的空间。先确保 s1 中需要访问的节点数更少,接下来访问 s1 中的每个节点 word。如果某个与节点 word 相邻的节点 neighbor 在 s2 中,则说明两个不同方向的搜索相遇,已经找到了一条起始节点和目标节点之间的最短路径,此时路径的长度就是它们之间的最短距离,否则将节点 neighbor 添加到 s3 中。当 s1 中所有的节点都访问完毕,接下来可能会访问 s1 的节点的相邻节点,即 s3 中的节点,因此将 s1 指向 s3。然后继续从 s1 和 s2 中选择一个节点数目少的方向进行新一轮的搜索。每轮搜索都意味着在起始节点和目标节点之间的最短路径上多前进了一步,因此变量 length 增加 1

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

测试计划和测试报告

1、软件测试计划简介 测试计划&#xff0c;一般是主管写&#xff0c;在需求分析之后&#xff0c;测试工作开始之间做的一些准备划工作。一般包含以下内容&#xff1a;5W1H 目的、测试范围、测试进度安排、测试人员、测试环境、测试方法工具&#xff0c;风险评估 &#xff08;w…

ABB、FANUC机器人点位加速度用法

机器人在点位与点位之间的运动&#xff0c;会存在速度上的变化&#xff0c;加速度指令的添加可以减小机器人在运动中&#xff0c;由高速到低速间惯性的带来的影响&#xff0c;修正机器人的路径误差&#xff0c;让机器人的运动更加顺滑。 一、ABB机器人指令添加 ABB机器人加速…

TCHouse-C

一.概括 1.地域&#xff08;Region&#xff09; 地域&#xff08;Region&#xff09;指腾讯云数据仓库 TCHouse-C 物理服务器所在的地理区域。腾讯云不同地域之间网络完全隔离&#xff0c;购买后不能更换。&#xff08;地域一旦选定&#xff0c;购买后无法更改。&#xff09;…

IP地址的主要功能及其在网络中的重要性

在当今数字化时代&#xff0c;互联网已经成为人们生活和工作中不可或缺的一部分。而IP地址&#xff08;Internet Protocol Address&#xff09;作为互联网中的关键组成部分&#xff0c;发挥着至关重要的作用。本文将探讨IP地址的主要功能以及其在网络中的重要性。 IP地址查询&…

解决在嵌入式系统开发编译时遇到的“no definition for ‘xxx‘在xxx.o文件中”BUG

提示&#xff1a;本文章是自己折腾嵌入式系统开发过程学习记录 解决在嵌入式系统开发编译时遇到的“no definition for xxx在xxx.o文件中”BUG BUG描述一、编译开发环境开发语言和库 二、问题分析知识点说明函数声明&#xff1a;函数定义&#xff1a; 三、解决方案1. 检查函数声…

el-image实现旋转修改保存

2024.4.8今天我学习了用el-image组件如何实现图片的旋转然后保存旋转之后的图片&#xff0c; 代码如下&#xff1a; 一、新增确定按钮。 <template><el-image src/assets/xxx/xxx clickclickImage(/assets/xxx/xxx)/> </template><script> export d…

redis的设计与实现(五)——独立功能

1. Redis的其他功能 redis 除了简单对对象的增删改查的功能之外&#xff0c;其实还有其他高级功能&#xff0c;了解这些内容有利于我们更灵活的使用 redis 完成我们的业务功能。 2. 发布与订阅 2.1. 基本概念 很多中间件都有发布与订阅功能&#xff0c;但是&#xff0c;作为一…

leetcode经典困难题-接雨水

. - 力扣&#xff08;LeetCode&#xff09; 42. 接雨水 困难 相关标签 相关企业 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,…

申请OV SSL证书

OV证书&#xff0c;即Organization Validation证书&#xff0c;是一种SSL/TLS证书类型&#xff0c;主要用于企业级应用&#xff0c;例如教育、政府、互联网等行业的大型企业和政府机关部门。与基础的域名验证&#xff08;DV&#xff09;证书相比&#xff0c;OV证书的验证过程更…

【笔记】mysql版本6以上时区问题

前言 最近在项目中发现数据库某个表的createTime字段的时间比中国时间少了13个小时&#xff0c;只是在数据库中查看显示时间不对&#xff0c;但是在页面&#xff0c;又是正常显示中国时区的时间。 排查 项目中数据库的驱动使用的是8.0.19&#xff0c;驱动类使用的是com.mysq…

【Canvas与艺术】绘制安布雷拉Umbrella伞公司标志

【关键点】 圆弧圆心的定位和起止角度。 【成果图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>安布雷拉Umbrella伞公司…

数字经济专家高泽龙担任工信部元宇宙标准化委员会委员

数字经济专家高泽龙受聘担任工信部元宇宙标准化委员会委员&#xff0c;出席工作组成立大会暨第一次全体委员会议。 第一届元宇宙国标、团标以及标委会工作组会议顺利召开&#xff01; 同时&#xff0c;正式成为工信部中国人工智能产业发展联盟科技伦理工作组成员&#xff01;

css 实现排行榜向上滚动

使用动画实现无线向上滚动 复制一层dom&#xff0c;使用动画向上滚动&#xff0c;鼠标hover的时候暂停动画 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthd…

【相机方案】智能驾驶的域控采用的“串行器和解串器”方案的总结(持续更新),SerDes,GMSL

SerDes是Serializer/Deserializer的缩写&#xff0c;即串行器和解串器。由于同轴线的传输延迟几乎可以忽略不计&#xff08;ns级别&#xff09;&#xff0c;相当于将原来只能短距离传输的高速并行信号(MIPI/I2C/CLK等)的传输距离延长&#xff0c;真正做到高带宽、低延迟、长距离…

潍微科技-水务信息管理平台 ChangePwd SQL注入漏洞复现(CNVD-2024-14945)

0x01 免责声明 请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;作者不为此承担任何责任。工具来自网络&#xff0c;安全性自测&#xff0c;如有侵权请联系删…

计算机网络书籍--《网络是怎样连接的》阅读笔记

第一章 浏览器生成信息 1.1 生成HTTP请求信息 1.1.1 URL Uniform Resource Locator, 统一资源定位符。就是网址。 不同的URL能够用来判断使用哪种功能来访问相应的数据&#xff0c;比如访问Web服务器就要用”http:”&#xff0c;而访问FTP服务器用”ftp:”。 FTP&#xff…

QT常用控件

常用控件 控件概述QWidget 核⼼属性核⼼属性概览enabledgeometrywindowTitlewindowIconwindowOpacitycursorfonttoolTipfocusPolicystyleSheet 按钮类控件Push ButtonRadio ButtionCheck Box 显⽰类控件LabelLCD NumberProgressBarCalendar Widget 输⼊类控件Line EditText Edi…

每日一题:缺失的第一个正数

给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xff1a;3 解释&#xff1a;范围 [1,2] 中的数字都在数组…

【微服务】------架构设计及常用组件

前言 在当今迅猛发展的软件开发领域&#xff0c;微服务架构已经成为构建灵活、可扩展系统的关键方法之一。本文将带领读者深入了解微服务架构的核心思想&#xff0c;并介绍构建这一架构所需的常用组件&#xff0c;为各位开发者提供全面的指导和洞察力。 BigDiagram 我们从一…

2024年4月8日腾讯云故障复盘及情况说明

2024年4月8日15点23分&#xff0c;腾讯云团队收到告警信息&#xff0c;云API服务处于异常状态&#xff1b;随即在腾讯云工单、售后服务群以及微博等渠道开始大量出现腾讯云控制台登录不上的客户反馈。 经过故障定位发现&#xff0c;客户登录不上控制台正是由云API异常所导致。云…