Python - 深夜数据结构与算法之 Two-Ended BFS

目录

一.引言

二.双向 BFS 简介

1.双向遍历示例

2.搜索模版回顾

三.经典算法实战

1.Word-Ladder [127]

2.Min-Gen-Mutation [433]

四.总结


一.引言

DFS、BFS 是常见的初级搜索方式,为了提高搜索效率,衍生了剪枝、双向 BFS 以及 A* 即启发式搜索等高级搜索方式。剪枝通过避免不必要或者次优解来减少搜索的次数,提高搜索效率;双向 BFS 通过层序遍历从首尾逼近答案,提高搜索效率;启发式搜索则是从优先级的角度出发,基于优先级高低搜索,提高搜索效率。本文主要介绍双向 BFS 的使用。

二.双向 BFS 简介

1.双向遍历示例

◆  双向连通图

求 A -> L 所需最短路径。

◆  遍历层级关系

不同颜色代表不同层级的 BFS,绿色为 root,蓝色为第二层,从左向右递推。

◆  双向遍历

从 A/L 同时层序遍历,当二者扩散的点重合时,左右路径长度相加即为最短路径。

2.搜索模版回顾

◆ DFS - 递归

◆ DFS - 非递归 

◆ BFS - 栈 

三.经典算法实战

1.Word-Ladder [127]

单词接龙: https://leetcode.cn/problems/word-ladder/description/

◆ 单向 BFS

class Solution:    def ladderLength(self, beginWord, endWord, wordList):""":type beginWord: str:type endWord: str:type wordList: List[str]:rtype: int"""valid_word = set(wordList)if endWord not in valid_word:return 0stack = [(beginWord, 1)]while stack:word, level = stack.pop(0)for i in range(len(word)):for char in "abcdefghijklmnopqrstuvwxyz":new_word = word[:i] + char + word[i + 1:]if new_word == endWord:return level + 1elif new_word in valid_word:stack.append((new_word, level + 1))valid_word.remove(new_word)return 0

这里我们可以打印一下转换的流程图,hot 有多层 level 出发,第二条路径走到了 cog,即结束遍历,当然 log 也可以走到 cog 只不过已经不需要了。

hot 2 -> lot 3

hot 2 -> dot 3 -> dog 4 -> cog 5

hot 2 -> dot 3 -> log 4 

◆ 双向 BFS 

class Solution(object):def ladderLength(self, beginWord, endWord, wordList):""":type beginWord: str:type endWord: str:type wordList: List[str]:rtype: int"""# 去重使用valid_word = set(wordList)# 边界条件if endWord not in wordList or len(wordList) == 0:return 0# 双向 BFSbegin, end, step = {beginWord}, {endWord}, 1# 同时有元素才能继续,如果一遍没元素代表已中断,无法联通,直接结束while begin and end:# 减少排查的可能性,从单词少的方向排查,避免无效查询if len(begin) > len(end):begin, end = end, begin# 存储下一层next_level = set()# 遍历下一层的多个结果for word in begin:# 遍历每个位置for i in range(len(word)):# a-zfor char in "abcdefghijklmnopqrstuvwxyz":# 节省无必要的替换if char != word[i]:new_word = word[:i] + char + word[i + 1:]# 二者相遇即返回if new_word in end:return step + 1if new_word in valid_word:next_level.add(new_word)valid_word.remove(new_word)# 指针替换begin = next_levelstep += 1return 0

已经将详细的注释加在代码里了,从 {start},{end} 两个方向查找,每次只找短的缩小无效查询的次数,这其实也是一种剪枝的策略,正所谓图中有真意欲辨已忘言:

◆ 双向 BFS + 剪枝

class Solution(object):def ladderLength(self, beginWord, endWord, wordList):""":type beginWord: str:type endWord: str:type wordList: List[str]:rtype: int"""# 去重使用valid_word = set(wordList)if endWord not in wordList or len(wordList) == 0:return 0# 剪枝优化s = set()for word in wordList:for char in word:s.add(char)s = ''.join(list(s))# 双向 BFSbegin, end, step = {beginWord}, {endWord}, 1while begin and end:if len(begin) > len(end):begin, end = end, begin# 存储下一层next_level = set()for word in begin:for i in range(len(word)):# a-zfor char in s:# 节省无必要的替换if char != word[i]:new_word = word[:i] + char + word[i + 1:]if new_word in end:return step + 1if new_word in valid_word:next_level.add(new_word)valid_word.remove(new_word)# 指针替换begin = next_levelstep += 1return 0

上面的两个方法在构建 new_word 时都遍历了所有 26 个字母 char,其实我们可以根据 end_word 的去重字符进行状态空间压缩,从而减少无意义的遍历,因为 char not in end_word 则 new_word 必定 not in end_word,从而优化时间复杂度。 

2.Min-Gen-Mutation [433]

最小基因突变: https://leetcode.cn/problems/minimum-genetic-mutation/description/ 

◆ BFS

class Solution(object):def minMutation(self, startGene, endGene, bank):""":type startGene: str:type endGene: str:type bank: List[str]:rtype: int"""if not bank:return -1bank = set(bank)if endGene not in bank:return -1stack = [(startGene, 0)]while stack:gene, level = stack.pop(0)for i in range(len(gene)):for char in "ACGT":new_gene = gene[:i] + char + gene[i + 1:]if new_gene == endGene:return level + 1if new_gene in bank:stack.append((new_gene, level + 1))bank.remove(new_gene)return -1

和上一题异曲同工之妙,只不过从单词接龙变成基因 🧬 接龙,每次修改的地方有限。

◆ 双向 BFS

class Solution(object):def minMutation(self, startGene, endGene, bank):""":type startGene: str:type endGene: str:type bank: List[str]:rtype: int"""if not bank:return -1bank = set(bank)if endGene not in bank:return -1# 初始化首尾front, back, step = {startGene}, {endGene}, 0while front and back:next_front = set()# 遍历当前层 Genefor gene in front:print(gene)for i in range(len(gene)):for char in "ACGT":new_gene = gene[:i] + char + gene[i + 1:]# 相遇了if new_gene in back:return step + 1# 下一层突变if new_gene in bank:next_front.add(new_gene)bank.remove(new_gene)# 取短的遍历加速if len(next_front) > len(back):front, back = back, next_frontelse:front = next_frontstep += 1return -1

和上面异曲同工,老曲新唱,相当于再温习一遍。其加速点就是左右替换,优先遍历可能性少的情况。

四.总结

这节内容 '双向 BFS' 起始也包含着很多剪枝的策略,所以其也属于优化搜索方式的方法之一,下一节我们介绍高级搜索的最后一块内容: A* 启发式搜索。

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

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

相关文章

【办公技巧】Word中如何对齐选择题中的ABCD选项?

使用word文件制作试卷,如何将ABCD选项全部设置对齐?除了一直按空格或者Tab键以外,还有其他方法吗?今天分享如何将ABCD选项对齐。 首先,我们打开【替换和查找】,在查找内容输入空格,然后点击全部…

2024年工信部AI人工智能证书“计算机视觉工程师”证书报考中!

为进一步贯彻落实中共中央印发《关于深化人才发展体制机制改革的意见》和国务院印发《关于“十四五”数字经济发展规划》等有关工作的部署求,深入实施人才强国战略和创新驱动发展战略,加强全国数字化人才队伍建设,持续推进人工智能专业人员能…

FreeRTOS——任务管理

学习目标 理解任务管理机制掌握动态任务创建掌握任务删除掌握任务挂起和恢复了解静态任务创建了解任务调度机制了解临界区的概念学习内容 任务处理常见操作 操作 API 动态任务创建 xTaskCreate 任务删除 vTaskDelete 静态任务创建 vTaskCreateStatic 挂起任务 vTaskSuspend 恢…

视频做成二维码查看?多格式视频二维码生成器的使用方法

现在音视频是工作和生活中经常需要使用的一种内容表现形式,很多人都通过这种方式来查看视频内容,比如产品介绍、使用说明、安装教程等。通过一个二维码就可以来承载视频内容,与传统的方式相比拥有更快的内容传播速度,简化用户获取…

【刷题笔记2】

刷题笔记2 最小公倍数、最大公约数 两个数的最大公约数两数乘积/最小公倍数 #<include> cmath; int a,b; int mgcd(a,b);//求最大公约数复制字符串substr()函数 s.substr(pos, len) &#xff1a;pos的默认值是0&#xff0c;len的默认值是s.size() - pos string a1;in…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷⑧

2023年全国职业院校技能大赛&#xff08;高职组&#xff09; “云计算应用”赛项赛卷8 目录 需要竞赛软件包环境以及备赛资源可私信博主&#xff01;&#xff01;&#xff01; 2023年全国职业院校技能大赛&#xff08;高职组&#xff09; “云计算应用”赛项赛卷8 模块一 …

创业只有破釜沉舟才能成功吗?2024个人创业做什么?2024普通人如何创业?

第一次创业必须零成本&#xff0c;千万别被那些砸锅卖铁、卖车卖房创业最后发了大财的鸡汤故事洗了脑&#xff0c;否则你一定会血本无归&#xff0c;妻离子散。 如果你要创业&#xff0c;请记住这组数据&#xff0c;全国能活过三年的创业公司只有10%&#xff0c;这10%不等于已经…

C# 自定义配置文件序列化生成+文件格式错误自动回档

文章目录 前言选择Xml简单的Xml使用测试用例简单的写简单的读简单的生成配置修改配置类测试用例运行结果对比 代码逻辑封装逻辑示意封装好的代码测试生成配置文件格式错误测试使用默认值覆盖来解决问题 配置文件人为修改错误如何解决解决方案代码测试用例运行结果 代码封装总结…

uni-app实现一键登录(企业版:因为获取手机号功能**目前针对非个人开发者**,所以个人开发者无法唤起获取手机号界面)

微信授权登录 用户在使用小程序时&#xff0c;其实已登录微信&#xff0c;其本质上就是&#xff1a;微信授权给小程序读取微信用户信息。 获取登录凭证&#xff08;企业版&#xff09; 前端&#xff1a;调用 wx.login() 接口获取登录凭证&#xff08;code&#xff09;。 后…

数据结构期末复习笔记

文章目录 数据结构期末复习第一章&#xff1a;数据结构绪论第二章&#xff1a;顺序表与单链表第三章&#xff1a;其它链表第四章&#xff1a;栈如何中缀转后缀后缀如何计算 第五章&#xff1a;队列第六章&#xff1a;串第七章&#xff1a;树的概念和遍历第八章&#xff1a;赫夫…

muduo网络库剖析——网络地址InetAddress类

muduo网络库剖析——网络地址InetAddress类 前情从muduo到my_muduo 概要socketaddr_in介绍成员用法 网络地址转换函数 框架与细节成员函数使用方法 源码结尾 前情 从muduo到my_muduo 作为一个宏大的、功能健全的muduo库&#xff0c;考虑的肯定是众多情况是否可以高效满足&…

flutter的状态管理学习

文章目录 1.flutter widget分类2. 代理组件又分为3. 状态 state 数据4. 刷新 数据变化5. code 1.flutter widget分类 组合渲染代理 2. 代理组件又分为 Positioned向父组件传递数据InheritedWidget向子组件传递数据 3. 状态 state 数据 状态就是用到了向子组件传递数据&#xff…

在矩池云使用微调ChatGLM3-6B教程

今天给大家介绍下如何在矩池云使用ChatGLM3-6B模型。 1 简介 GitHub&#xff1a;https://github.com/THUDM/ChatGLM3 魔搭&#xff1a;https://modelscope.cn/models/ZhipuAI/chatglm3-6b/ ChatGLM3 是智谱AI和清华大学 KEG 实验室联合发布的新一代对话预训练模型。ChatGLM3…

租房别再傻傻的扯网线了!随身WiFi靠谱品牌推荐,哪个随身WiFi最好用

如果你是租房党&#xff0c;并且预算有限的话&#xff0c;拉网线太麻烦了&#xff0c;价格很贵&#xff0c;需要搬家的时候还要重新扯线&#xff0c;事儿很多。想买一个网速快&#xff0c;便捷的随身WiFi看电影、刷刷抖音、打打游戏&#xff0c;那一定一定要认真看完我这篇文章…

NSIS 安装windows 安装包(包括QT和MFC)

NSIS&#xff08;Nullsoft Scriptable Install System&#xff09;是一个开源的 Windows 系统下安装程序制作程序。它提供了安装、卸载、系统设置、文件解压缩等功能。 基本概念 区段 是对应某种安装/卸载选项的处理逻辑&#xff0c;该段代码仅当用户选择相应的选项才被执行…

metrics安装异常原因【doesn‘t contain any IP SANs】

1、问题背景 安装好k8s后&#xff0c;安装metrics-server后发现对应的pod一直无法启动。 apiVersion: v1 kind: ServiceAccount metadata:labels:k8s-app: metrics-servername: metrics-servernamespace: kube-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: Cl…

Jasper report InputStream动态生产Logo

第一步&#xff0c;新建一个Parameter 新建一个对象Parameter&#xff0c;类型为java.io.InputStream 第二步&#xff0c;拖拽Image对象 拖拽Image对象&#xff0c;并调整长宽&#xff0c;Image下选择Expression $P{Logo_Blue} 第三步&#xff0c;把图片转换成stream rptHea…

numpy100练习题,包含相应使用函数解释

取自github开源项目&#xff1a;numpy100题 文章目录 1. 导入numpy库并简写为 np (★☆☆)2. 打印numpy的版本和配置说明 (★☆☆)3. 创建一个长度为10的空向量 (★☆☆)4. 如何找到任何一个数组的内存大小&#xff1f; (★☆☆)5. 如何从命令行得到numpy中add函数的说明文档?…

D41|打家劫舍

198.打家劫舍 初始思路&&题解复盘&#xff1a; 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i]&#xff1a;考虑下标i&#xff08;包括i&#xff09;以内的房屋&#xff0c;最多可以偷窃的金额为dp[i]。 2.确定递推公式 决定dp[i]的因素就是第i房…

resulttype和parametertype的区别

Mybatis的Mapper文件中的select,insert,update,delect元素中都有一个parameterType和resultType属性,parameterType属性用于对应的mapper接口方法接受的参数类型,resultType用于指定sql输出的结果类型 resultType与parameterType 的基本使用的区别 &#xff1a; 1、使用resul…