词梯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. RabbitMQ之消息模式2

    消费端限流 什么是消费端的限流? 假设一个场景,首先,我们RabbitMQ服务器有上万条未处理的消息,我们随便打开一个消费者客户端,会出现下面情况: 巨量的消息瞬间全部推送过来,但是我们单个客户端无法 ...

  2. NAT-T下的端口浮动

    1. IKE端口浮动 IPsec在隧道建立第一第二阶段主要进行加密方式.加密策略等信息的协商,这部分功能是通过IKE协议来实现的. IKE协议默认端口为500,但是如果IPsec隧道传输路径上存在NA ...

  3. Input 只能输入数字,数字和字母等的正则表达式

    JS只能输入数字,数字和字母等的正则表达式 1.文本框只能输入数字代码(小数点也不能输入) <input onkeyup="this.value=this.value.replace( ...

  4. 处理器核、Core、处理器、CPU区别&&指令集架构与微架构的区别&&32位与64位指令集架构说明

    1.处理器核.Core.处理器.CPU的区别 严格来说"处理器核"和" Core "是指处理器内部最核心的部分,是真正的处理器内核:而"处理器&quo ...

  5. 74cms v3.3 后台SQL注入

    注入存在于后台 admin_baiduxml.php 代码 52-63行 elseif($act == 'setsave') { $_POST['xmlmax']=intval($_POST['xml ...

  6. 5-7接口测试工具之jmeter的使用

    1.安装 免费的,安装jdk配好系统环境变量就能用了 2.jmeter测接口 获取用户信息,接口文档定义有2种请求方式 添加线程组-->添加http请求-->输入接口文档中说明的服务器名称 ...

  7. composer 包 slim使用案例,一个简单的路由解决方案

    nginx配置文件修改 location / { try_files $uri /index.php$is_args$args; } 设置好nginx伪静态,把所有的请求方式都转向到index.php ...

  8. POJ——3278 Catch That Cow(BFS队列)

    相比于POJ2251的三维BFS,这道题做法思路完全相同且过程更加简单,也不需要用结构体,check只要判断vis和左右边界的越界情况就OK. 记得清空队列,其他没什么好说的. #include< ...

  9. 5.21学习总结——android开发实现用户头像的上传

    最近在做个人头像的上传,具体是能调用摄像头和从相册进行选择.本篇文章参考的我的同学的博客,大家有兴趣可以去原作者那里去看看: Hi(.・∀・)ノ (cnblogs.com) 1.使用glide进行图片 ...

  10. python math详解(1)

    python math详解(1) 一.导入 python要调用math要进行导入 import math 二.返回值 math包里有一些值 比如 math.pi 返回pi的值 约为3.14 math. ...