词梯Word Ladder问题

  • 要求是相邻两个单词之间差异只能是1个字母,如FOOL变SAGE:
FOOL >> POOL >> POLL >> POLE >> PALE>> SALE >> SAGE
  • 目标是找到最短的单词变换序列

    • 用图表示单词之间的关系;
    • 用一种名为广度优先搜索 BFS的图算法找到从起始单词到结束单词的最短路径。

构建词梯图

算法

  • 首先是将所有单词作为顶点加入图中,再设法建立顶点之间的边
  • 对每个顶点(单词) , 与其它所有单词进行比较, 如果相差仅1个字母, 则建立一条边
  • 时间复杂度是O(n²),对于所有4个字母的5110个单词,需要超过2600万次比较

优化算法

  • 改进的算法是创建大量的桶, 每个桶可以存放若干单词

    • 桶标记是去掉1个字母,通配符“_”占空的单词
  • 所有匹配标记的单词都放到这个桶里
    • 所有单词就位后,再在同一个桶的单词之间建立边即可
  • 单词关系图是一个非常稀疏的图

采用字典建立桶

def buildGraph(wordFile):
d = {}
g = Graph()
wfile = open(wordFile, 'r')
for line in wfile:
word = line[:-1]
for i in range(len(word)):
bucket = word[:i]+'_'+[i+1:]
if bucket in d:
d[bucket].append(word)
else:
d[bucket] = [word] for bucket in d.keys():
for word1 in d[bucket]:
for word2 in d[bucket]:
if word1 != word2:
g.addEdge(word1, word2) return g

广度优先搜索 BFS (breadth first search)

在单词关系图建立完成以后, 需要继续在图中寻找词梯问题的最短序列

算法思路

  • 给定图G, 以及开始搜索的起始顶点s

    • BFS搜索所有从s可到达顶点的边
    • 而且在达到更远的距离k+1的顶点之前, BFS会找到全部距离为k的顶点
    • 可以想象为以s为根,构建一棵树的过程,从顶部向下逐步增加层次
    • 广度优先搜索能保证在增加层次之前,添加了所有兄弟节点到树中
  • 为了跟踪顶点的加入过程, 并避免重复顶点, 要为顶点增加3个属性
    • 距离distance:从起始顶点到此顶点路径长度;
    • 前驱顶点predecessor:可反向追溯到起点;
    • 颜色color:
      • 标识了此顶点是尚未发现(白色)
      • 已经发现(灰色)
      • 还是已经完成探索(黑色)
  • 还需要用一个队列Queue来对已发现的顶点进行排列

    决定下一个要探索的顶点(队首顶点)

算法过程

从起始顶点s开始, 作为刚发现的顶点,标注为灰色, 距离为0, 前驱为None,加入队列, 接下来是个循环迭代过程:

  • 从队首取出一个顶点作为当前顶点;
  • 遍历当前顶点的邻接顶点,如果是尚未发现的白色顶点,则将其颜色改为灰色(已发现),距离增加1,前驱顶点为当前顶点,加入到队列中
  • 遍历完成后,将当前顶点设置为黑色(已探索过),循环回到步骤1的队首取当前顶点





代码

  • 在以FOOL为起始顶点, 遍历了所有顶点, 并为每个顶点着色、 赋距离和前驱的代码
def bfs(g, start):
start.setDistance(0)
start.setPred(None)
vertQueue = Queue()
vertQueue.enqueue(start)
while(vertQueue.size > 0):
currentVert = vertQueue.dequeue()
for nbr in currentVert.getConnections():
if(nbr.getColor() == 'white'):
nbr.setColor('gray')
nbr.setDistance(currentVert.getDistance()+1)
nbr.setPred(currentVert)
vertQueue.enqueue(nbr)
currentVert.setColor('black')
  • 最后,通过一个回途追溯函数来确定FOOL到任何单词顶点的最短词梯!
def traverse(y):
x = y
while(x.getPred()):
print(x.getId())
x = x.getPred()
print(x.getId())

完整代码

def buildGraph(wordFile):
d = {}
g = Graph()
wfile = open(wordFile, 'r')
for line in wfile:
word = line[:-1]
for i in range(len(word)):
bucket = word[:i]+'_'+[i+1:]
if bucket in d:
d[bucket].append(word)
else:
d[bucket] = [word] for bucket in d.keys():
for word1 in d[bucket]:
for word2 in d[bucket]:
if word1 != word2:
g.addEdge(word1, word2) return g def bfs(g, start):
start.setDistance(0)
start.setPred(None)
vertQueue = Queue()
vertQueue.enqueue(start)
while(vertQueue.size > 0):
currentVert = vertQueue.dequeue()
for nbr in currentVert.getConnections():
if(nbr.getColor() == 'white'):
nbr.setColor('gray')
nbr.setDistance(currentVert.getDistance()+1)
nbr.setPred(currentVert)
vertQueue.enqueue(nbr)
currentVert.setColor('black') def traverse(y):
x = y
while(x.getPred()):
print(x.getId())
x = x.getPred()
print(x.getId()) if __name__ == "__main__":
wordgrah = buildGraph("fourletterwords.txt")
bfs(wordgrah, wordgrah.getVertex('FOOL'))
traverse(wordgrah.getVertex('SAGE'))

算法分析

  • BFS算法主体是两个循环的嵌套

    • while循环对每个顶点访问一次,所以是O(|V|)
    • 而嵌套在while中的for,由于每条边只有在其起始顶点u出队的时候才会被检查一次
    • 而每个顶点最多出队1次,所以边最多被检查1次,一共是O(|E|)
    • 综合起来BFS的时间复杂度为O(|V|+|E|)
  • 词梯问题还包括两个部分算法
    • 建立BFS树之后, 回溯顶点到起始顶点的过程,最多为O(|V|)
    • 创建单词关系图也需要时间,最多为O(|V|2)

【数据结构与算法Python版学习笔记】图——词梯问题 广度优先搜索 BFS的更多相关文章

  1. 【数据结构与算法Python版学习笔记】目录索引

    引言 算法分析 基本数据结构 概览 栈 stack 队列 Queue 双端队列 Deque 列表 List,链表实现 递归(Recursion) 定义及应用:分形树.谢尔宾斯基三角.汉诺塔.迷宫 优化 ...

  2. 【数据结构与算法Python版学习笔记】引言

    学习来源 北京大学-数据结构与算法Python版 目标 了解计算机科学.程序设计和问题解决的基本概念 计算机科学是对问题本身.问题的解决.以及问题求解过程中得出的解决方案的研究.面对一 个特定问题,计 ...

  3. 【数据结构与算法Python版学习笔记】图——最短路径问题、最小生成树

    最短路径问题 概念 可以通过"traceroute"命令来跟踪信息传送的路径: traceroute www.lib.pku.edu.cn 可以将互联网路由器体系表示为一个带权边的 ...

  4. 【数据结构与算法Python版学习笔记】图——强连通分支

    互联网 我们关注一下互联网相关的非常巨大图: 由主机通过网线(或无线)连接而形成的图: 以及由网页通过超链接连接而形成的图. 网页形成的图 以网页(URI作为id)为顶点,网页内包含的超链接作为边,可 ...

  5. 【数据结构与算法Python版学习笔记】图——骑士周游问题 深度优先搜索

    骑士周游问题 概念 在一个国际象棋棋盘上, 一个棋子"马"(骑士) , 按照"马走日"的规则, 从一个格子出发, 要走遍所有棋盘格恰好一次.把一个这样的走棋序列 ...

  6. 【数据结构与算法Python版学习笔记】图——拓扑排序 Topological Sort

    概念 很多问题都可转化为图, 利用图算法解决 例如早餐吃薄煎饼的过程 制作松饼的难点在于知道先做哪一步.从图7-18可知,可以首先加热平底锅或者混合原材料.我们借助拓扑排序这种图算法来确定制作松饼的步 ...

  7. 【数据结构与算法Python版学习笔记】图——基本概念及相关术语

    概念 图Graph是比树更为一般的结构, 也是由节点和边构成 实际上树是一种具有特殊性质的图 图可以用来表示现实世界中很多有意思的事物,包括道路系统.城市之间的航班.互联网的连接,甚至是计算机专业的一 ...

  8. 【数据结构与算法Python版学习笔记】查找与排序——散列、散列函数、区块链

    散列 Hasing 前言 如果数据项之间是按照大小排好序的话,就可以利用二分查找来降低算法复杂度. 现在我们进一步来构造一个新的数据结构, 能使得查找算法的复杂度降到O(1), 这种概念称为" ...

  9. 【数据结构与算法Python版学习笔记】算法分析

    什么是算法分析 算法是问题解决的通用的分步的指令的聚合 算法分析主要就是从计算资源的消耗的角度来评判和比较算法. 计算资源指标 存储空间或内存 执行时间 影响算法运行时间的其他因素 分为最好.最差和平 ...

随机推荐

  1. kubeadm方式搭建K8S集群

    一.kubeadm介绍 二.安装要求 三.集群规划 四.环境初始化(在每个服务器节点操作) 1.关闭防火墙 2.关闭selinux 3.关闭swap 4.根据规划设置主机名 5.在Master添加ho ...

  2. Python - 面向对象编程 - self 参数

    为什么要讲 self 参数 class PoloBlog: def __init__(self): ... def say(self): ... 在类里面,所有实例方法都需要加 self 参数,且排在 ...

  3. Devexpress TreeList控件使用

    增加显示列treeList1.Columns.Add(new TreeListColumn {Caption = "城市", FieldName = "Name" ...

  4. JS010. 三元运算符扩展运用(多层判断语句 / 多条表达式)

    MDN - 三元运算符 语法 Condition ? exprIfTrue : exprIfFalse 用例: function getFee(isMember) { return(isMember ...

  5. aes加解密后续问题contentType不是application/json时候后台解析请求对象request

    一.post请求的三种content-type 1.application/x-www-form-urlencoded 主要用于如下:1.1: 最常见的POST提交数据方式.1.2:原生form默认的 ...

  6. 菜狗、《灵笼》、《时光代理人》,重新审视Z世代的电商逻辑

    来源:懂懂笔记 B站还有多少潜力可以挖掘? 虽然B站的最新财报依然还是亏损,但同时也让人看到更多的可能性. 从财报数据的亮点来看,一是营收增长,B站二季度营收为44.95亿元,同比增长72%.营收上B ...

  7. sqlmap --eval 用法

    今天查资料发现 sqlmap 有一个 --eval 的参数 --eval=EVALCODE Evaluate provided Python code before the request (e.g. ...

  8. linux主机安全加固-个人经验

    说明:我并没有一个系统的网络安全知识体系,随笔里面提到的内容是个人在从事运维行业这几年中总结出来的一点经验,仅供大家参考. 说到linux主机安全加固,我可以想到的就是三个方向吧,基线整改.访问控制和 ...

  9. SpringBoot系列——附件管理:整合业务表单实现上传、回显、下载

    前言 日常开发中,大多数项目都会涉及到附件上传.回显.下载等功能,本文记录封装通用附件管理模块,并与业务模块进行整合实现上传.回显.下载 我们之前已经对文件上传下载有过记录,传送门:基于"f ...

  10. Jmeter系列(24)- 常用逻辑控制器(3) | 模块控制器Module Controller

    模块控制器(Module Controller) 作用 可以理解为引用.调用执行的意思,调用范围为testplan树下任意的逻辑控制器,模块控制器除外 点开testplan树,需要引用哪个逻辑控制器选 ...