参考自 MOOC数据结构与算法Python版

目录

  • 一、广度优先搜索
    • 1.1 BFS算法过程
    • 1.2 广度优先搜索算法分析
  • 二、深度优先搜索
    • 2.1 骑士周游
      • 2.1.1 问题定义
      • 2.1.2 构建骑士周游图
      • 2.1.3 构建走棋关系图
      • 2.1.4 骑士周游问题算法实现
      • 2.1.5 骑士周游问题算法分析与改进
    • 2.2 通用的深度优先搜索
    • 2.3 通用的深度优先搜索算法分析

一、广度优先搜索

在单词关系图建立完成以后, 需要继续在图中寻找词梯问题的最短序列,即要用到“广度优先搜索Breadth First Search”算法对单词关系图进行搜索。BFS是搜索图的最简单算法之一, 也是其它一些重要的图算法的基础

  1. 给定图G, 以及开始搜索的起始顶点s
  2. BFS搜索所有从s可到达顶点的,距离为1
  3. 在达到更远的距离k+1的顶点之前, BFS会找到全部距离为k的顶点

可以想象为以s为根,构建一棵树的过程,从顶部向下逐步增加层次,广度优先搜索能保证在增加层次之前,添加了所有兄弟节点到树中

1.1 BFS算法过程

为了跟踪顶点的加入过程, 并避免重复顶点, 要为顶点增加以下属性:

  • 距离distance:从起始顶点到此顶点路径长度;
  • 前驱顶点predecessor:可反向追溯到起点;
  • 颜色color:标识了此顶点是尚未发现(白色)、已经发现(灰色)、还是已经完成探索(黑色)
  • 队列Queue来对已发现的顶点进行排列,决定下一个要探索的顶点(队首顶点)

步骤:
1. 从起始顶点s开始, 作为刚发现的顶点,标注为灰色, 距离为0, 前驱为None,
加入队列, 接下来是个循环迭代过程:
2. 从队首取出一个顶点作为当前顶点;
3. 遍历当前顶点的邻接顶点,如果是尚未发现的白色顶点,则将其颜色改为灰色(已发现),距离增加1,前驱顶点为当前顶点,加入到队列中
4. 遍历完成后,将当前顶点设置为黑色(已探索过),循环回到步骤2的队首取当前顶点

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为起始顶点, 遍历了所有顶点, 并为每个顶点着色赋距离前驱之后,即可以通过一个回途追溯函数来确定FOOL到任何单词顶点的最短词梯

def traverse(y):x = ywhile x.getPred():print(x.getId())x = x.getPred()print(x.getId())
wordGraph = buildGraph("test.txt")
bfs(wordGraph, wordGraph.getVertex('FOOl'))
traverse(wordGraph.getVertex)

1.2 广度优先搜索算法分析

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

  • while循环对每个顶点访问一次,所以是O(∣V∣)O(|V|)O(∣V∣)
  • 嵌套在while中的for,由于每条边只有在其起始顶点u出队的时候才会被检查一次,而每个顶点最多出队1次,所以边最多被检查1次,一共是O(∣E∣)O(|E|)O(∣E∣)

综合起来BFS的时间复杂度为O(∣V∣+∣E∣)O(|V|+|E|)O(∣V∣+∣E∣)

词梯问题还包括两个部分算法:

  • 建立BFS树之后, 回溯顶点到起始顶点的过程,最多为O(|V|)
  • 创建单词关系图也需要时间,最多为O(|V|2)

二、深度优先搜索

2.1 骑士周游

2.1.1 问题定义

在一个国际象棋棋盘上, 一个棋子“马”(骑士) , 按照“马走日”的规则, 从一个格子出发, 要走遍所有棋盘格恰好一次。把一个这样的走棋序列称为一次“周游”

在8×8的国际象棋棋盘上, 合格的“周游”数量有1.305×1035这么多, 走棋过程中失败的周游就更多了
采用图搜索算法, 是解决骑士周游问题最容易理解和编程的方案之一.解决方案还是分为两步:

  1. 首先将合法走棋次序表示为一个图
  2. 采用图搜索算法搜寻一个长度为(行×列-1)的路径,路径上包含每个顶点恰一次

2.1.2 构建骑士周游图

将棋盘和走棋步骤构建为图的思路:

  1. 将棋盘格作为顶点
  2. 按照“马走日”规则的走棋步骤作为连接边
  3. 建立每一个棋盘格的所有合法走棋步骤能够到达的棋盘格关系图
def genLegalMoves(x, y, bdSize):newMoves = []# 马走日的八个格子,“-”表示向左moveOffsets = [(-1,-2),(-1,2),(-2,-1),(-2,-1),(1, -2),(1, 2),(2, -1),(2, 1)]for i in moveOffsets:newX = x + i[0]newY = y = i[1]if legalCoord(newX, bdSize) and legalCoord(newY, bdSize):newMoves.append((newX,newY))return newMoves
def legalCoord(x, bdSize): #确定不会走出棋盘if 0< x < bdSize:return Trueelse: return False

2.1.3 构建走棋关系图

def knightGraph(bdSize): #bdSize表示正方形棋盘边的大小ktGraph = Graph()for row in range(bdSize): #遍历每个格子for col in range(bdSize):nodeId = posToNodeId(row,col,bdSize)newPositions = genLegalMoves(row,col,bdSize)for e in newPositions:nid = posToNodeId(e[0],e[1],bdSize)ktGraph.addEdge(nodeId,nid) #添加边及顶点return ktGraph
def posToNodeId(row, col, bdSize): #每个格子生成一个Idreturn row*bdSize + col

骑士周游图: 8×8棋盘生成的图,具有336条边, 相比起全连接的4096条边, 仅8.2%, 还是稀疏图

2.1.4 骑士周游问题算法实现

用于解决骑士周游问题的图搜索算法是深度优先搜索(Depth First Search),相比前述的广度优先搜索, 其特点是逐层建立搜索树,深度优先搜索是沿着树的单支尽量深入向下搜索,如果到无法继续的程度还未找到问题解,就回溯上一层再搜索下一支。

DFS的两个实现算法

  • 一个DFS算法用于解决骑士周游问题,其特点是每个顶点仅访问一次
  • 另一个DFS算法更为通用,允许顶点被重复访问,可作为其它图算法的基础

深度优先搜索解决骑士周游的关键思路:

  • 如果沿着单支深入搜索到无法继续(所有合法移动都已经被走过了)时,路径长度还没有达到预定值(8×8棋盘为63)
  • 那么就清除颜色标记,返回到上一层
  • 换一个分支继续深入搜索

引入一个栈来记录路径,并实施返回上一层的回溯操作

def knightTour(n,path,u,limit): #层次,路径,当前顶点,搜索总深度u.setColor('grey')path.append(u) #将当前顶点加入路径if n < limit:nbrList = list(u.getConnections()) #对所有合法移动逐一深入i = 0done = Falsewhile i < len(nbrList) and not done:if nbrList[i].getColor() == "while": #选择未经过的顶点done = knightTour(n+1, path, nbrList[i], limit)#层次加1,递归深入i += 1if not done: #都无法完成总深度,回溯path.pop()u.setColor("white")else: done = Truereturn done

2.1.5 骑士周游问题算法分析与改进

上述算法的性能高度依赖于棋盘大小:就5×5棋盘,约1.5秒可以得到一个周游路径,但8×8棋盘,则要半个小时以上才能得到一个解,目前实现的算法, 其复杂度为O(kn)O(k^n)O(kn), 其中n是棋盘格数目.
对nbrList的灵巧构造,以特定方式排列顶点访问次序,可以使得8×8棋盘的周游路径搜索时间降低到秒级!这个改进算法被特别以发明者名字命名:Warnsdorff算法。

  • 初始算法中nbrList, 直接以原始顺序来确定深度优先搜索的分支次序
  • 新的算法, 仅修改了遍历下一格的次序
    将u的合法移动目标棋盘格排序为:具有最少合法移动目标的格子优先搜索,(先周边再中间)
 def orderByAvail(n):resList = []for v in n.getConnections():if v.getColor() == "white":c = 0for w in v.getConnections():if w.getColor() == 'white':c += 1resList.append((c,v))resList.sort(key = lambda x: x[0])return [y[1] for y in resList]

2.2 通用的深度优先搜索

骑士周游问题是一种特殊的对图进行深度优先搜索,其目的是建立一个没有分支的最深的深度优先树,表现为一条线性的包含所有节点的退化树。

  • 一般的深度优先搜索目标是在图上进行尽量深的搜索, 连接尽量多的顶点, 必要时可以进行分支(创建了树),有时候深度优先搜索会创建多棵树,称为“深度优先森林”。
  • 深度优先搜索同样要用到顶点的“前驱”属性, 来构建树或森林,另外要设置“发现时间”和“结束时间”属性,这两个新属性对后面的图算法很重要
    • 前者是在第几步访问到这个顶点(设置灰色)
    • 后者是在第几步完成了此顶点探索(设置黑色)
  • 带有DFS算法的图实现为Graph的子类
    • 顶点Vertex增加了成员Discovery及Finish
    • 图Graph增加了成员time,用于记录算法执行的步骤数目

BFS采用队列存储待访问顶点
DFS则是通过递归调用,隐式使用了栈

form pythonds.graphs import Graph
class DFSGraph(Graph):def __init__(self):super().__init__()self.time = 0def dfs(self):for aVertex in self:  #颜色初始化aVertex.setColor('white')aVertex.setPred(-1)for aVertex in self:if aVertex.getColor() == 'white':self.dfsvisit(aVertex)#只要未访问,则建立单棵森林def dfsvisit(self,startVertex):startVertex.setColor('gray')  #设置开始顶点为灰色self.time += 1 #算法的步数startVertex.setDiscovery(self.time)for nextVertex in startVertex.getConnections():if nextVertex.getColor() == 'white':nextVertex.setPred(startVertex)#只要未访问,则下一步以该节点为顶点self.dfsvisit(nextVertex)startVertex.setColor('black')self.time += 1startVertex.setFinish(self.time) #深度优先递归访问

DFS构建的树, 其顶点的“发现时间”和“结束时间”属性, 具有类似括号的性质

  • 即一个顶点的“发现时间”总小于所有子顶点的“发现时间
  • 而“结束时间”则大于所有子顶点“结束时间”比子顶点更早被发现,更晚被结束探索

2.3 通用的深度优先搜索算法分析

DFS运行时间同样也包括了两方面:

  • dfs函数中有两个循环,每个都是|V|次,所以是O(∣V∣)O(|V|)O(∣V∣)
  • 而dfsvisit函数中的循环则是对当前顶点所连接的顶点进行,而且仅有在顶点为白色的情况下才进行递归调用,所以对每条边来说只会运行一步,所以是O(∣E∣)O(|E|)O(∣E∣)

加起来就是和BFS一样的O(∣V∣+∣E∣)O(|V|+|E|)O(∣V∣+∣E∣)

数据结构与算法(python):广度优先搜索(Breadth First Search,BFS)和深度优先算法(Depth First Search,DFS)相关推荐

  1. 算法导论--广度优先搜索和深度优先搜索

    广度优先搜索 在给定图G=(V,E)和一个特定的源顶点s的情况下,广度优先搜索系统地探索G中的边,以期"发现"可从s 到达的所有顶点,并计算s 到所有这些可达顶点之间的距离(即最少 ...

  2. 常用算法2 - 广度优先搜索 深度优先搜索 (python实现)

    1. 图 定义:图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合. 简单点的说:图由节点和边组成.一 ...

  3. (造轮子)C 创建队列和图实现广度优先算法(BFS)和深度优先算法(DFS)(数据结构)

    链表.队列和图实现BFS和DFS算法(C+造轮子+详细代码注释) 1.队列的链式存储结构   队列的链式表示称为链队列,它实际上是一个同时带有队头指针和队尾指针的单链表.头指针指向队头节点,尾指针指向 ...

  4. 图的遍历 --- 广度优先搜索【借助队列实现】 + 深度优先搜索【借助递归栈】

    1. >>图的遍历是指从图中的某一顶点出发,按照某种搜索方法沿着图中的边对图中的所有顶点访问 一次且仅访问一次. 注意:树是一种特殊的图,所以树的遍历实际上也可以看作是一种特殊的图的遍历. ...

  5. JS算法:广度优先搜索(BSF)的理解

    广度优先搜索方法的实现 (基于<学习JavaScript数据结构与算法>补充学习) 实现步骤如下 1.用initializeColor函数来将color数组初始化为白色:也就是将每个节点初 ...

  6. java广度优先算法,算法之广度优先搜索

    一.引言 上一次介绍的算法是深度优先搜索 这次我们来研究一下广度优先搜索,看看怎么理解以及写出这个算法 这个算法需要数据结构的基础--队列,如果没有这个基础的同学去恶补一下. 二.小小问题 Q:在一个 ...

  7. 算法—12.广度优先搜索

    1.具体算法 /*** Created by huazhou on 2015/12/6.*/ public class TestSearch {public static void main(Stri ...

  8. 算法图解——广度优先搜索

    本文章根据<算法图解>提供来写的. # -*- coding = utf-8 -*- # @Time : 2021/12/14 17:20 # @Author : 宁洪康 # @File ...

  9. 小游戏系列算法之五广度优先搜索,双向广搜,八数码,华容道

    前段时间在玩仙五前,遇上了蚩尤冢拼图这个小游戏. 其实就是八数码问题,一直想着如何才能用最少步数求解,于是就写了个程序. Q1:什么是八数码问题? A1:首先假定一个3*3的棋盘(如上图),分别有1, ...

  10. C++一本通基础算法:广度优先搜索(BFS)

    tip:该算法将会疯狂使用队列,包括各种类型的队列 算法概述:先将起点入队,先向起点相邻的位置检索,如果满足条件,那么将这个位置入队. 然后将起点出队.再将所有与队首相邻且满足条件的位置入队,队首出队 ...

最新文章

  1. “Query结构化分析及槽位填充” byte dance NLP lambda在线讲的不错的
  2. 初识Java-IO流
  3. 如何挽回一个快要“变心”的买家?(转自索菲外贸笔记)
  4. android 取消点击GridView的时候出现的那个黄色背景
  5. 聊聊Function的bind()
  6. 神经网络中,正则化L1与L2的区别、如何选择以及代码验证
  7. 《Adobe Dreamweaver CS6中文版经典教程》——第2课 HTML基础2.1 什么是HTML
  8. 每日算法系列【LeetCode 122】买卖股票的最佳时机 II
  9. [UML]UML系列——类图class的实现关系Realization
  10. Matlab2017a汉化版软件详细安装教程
  11. 算法竞赛入门经典(第二版) 答案汇总(持续更新)
  12. linux librtmp 编译,linux安装python-librtmp
  13. linux 安装ros软路由,Centos7安装RouterOS软路由
  14. PostScript文件
  15. 动点四边形周长最短_中考数学之四边形周长最小值
  16. Python实现手机和电脑之间的文件传输
  17. 从前慢-Mysql高级及实战
  18. 免费图片转pdf的方法?学会图片转pdf很重要
  19. avalonia 控件TextBox 及其他控件文本改变事件
  20. 对VC 一些常见问题的整理

热门文章

  1. 解决体感控制模型穿模问题
  2. 【Arduino】I2C Master 实现
  3. 威海微联计算机软件,兰州网站快速排名-微联关键词快速排名优化
  4. 怎样做一个优秀的班主任
  5. Linux C语言运行库 glibc
  6. 互融云保理业务系统助力企业快速拓展业务
  7. 佳域G3乐蛙刷机包 第109期
  8. 东京奥运会微博阅读量高达 236 亿,看各大品牌都做了什么?
  9. Salesforce.com商业模式
  10. 白鹭引擎图片浏览工具