宽度优先搜索(BFS)是一种有效的图遍历方法,类似于二叉树的层遍历,其从初始搜索节点开始,逐层的往外扩散,特别适用于寻找两个节点间的最短距离等问题。
在BFS过程中,需要引入队列结构。在探索某节点时,将相邻节点依次入列;探索完后,再将这些节点依次出列,从而实现遍历的层次化。因此,对某个节点,其存在三种状态:(1)未探索阶段;(2)作为相邻节点入列,待探索节点;(3)探索完(即将其所有的相邻节点入列)后的阶段。
不同于二叉树的单向父子关系,无向图或有向图中均可能出现两邻接节点的双向指向关系。为了防止遍历过程中的循环,我们可以采用类似于【着色】的方式为节点在上述不同阶段进行标识。当某个节点未被探索时(即上面的状态1),才会入列。
同时为了回溯的方便,还可以引入前驱节点以及距初始节点距离的相关属性和方法。
下面就是BFS的基本代码:
# 1. 创建Vertex对象
class Vertex(object):
"""
节点对象,在基础节点对象的基础上,添加了关于着色、前驱节点以及距离的相关属性和方法
"""
def __init__(self, key):
self.key = key
self.connectedTo = {} # 存放指向的其它节点,以Vertex:连接边weight的
self.color = "white" # BFS中的状态标记
self.predecessor = None # BFS时前驱节点的标记
self.distance = 0 # 距离起始节点的距离标记
def addNeighbor(self, nbr, weight):
self.connectedTo.update({nbr: weight})
def __str__(self):
return str(self.key) + '-->' + str([nbr.key for nbr in self.connectedTo])
def getConnections(self):
return self.connectedTo.keys()
def getId(self):
return self.key
def getWeight(self, nbr):
weight = self.connectedTo.get(nbr)
if weight is not None:
return weight
else:
raise KeyError("No such nbr exist!")
def getColor(self):
return self.color
def setColor(self, color):
self.color = color
def setPredecessor(self, vertex):
self.predecessor = vertex
def getPredecessor(self):
return self.predecessor
def setDistance(self, dis):
self.distance = dis
def getDistance(self):
return self.distance
# 2. 创建Graph对象
class Graph(object):
"""
图对象
"""
def __init__(self):
self.vertexList = {} # 字典保存节点信息,以{key: Vertex}的方式
self.vertexNum = 0 # 统计图节点数
def addVertex(self, key):
self.vertexList.update({key: Vertex(key)})
self.vertexNum += 1
def getVertex(self, key):
vertex = self.vertexList.get(key)
return vertex
def __contains__(self, key):
return key in self.vertexList.values()
def addEdge(self, f, t, weight=0):
if not self.getVertex(f):
self.addVertex(f)
if not self.getVertex(t):
self.addVertex(t)
self.vertexList[f].addNeighbor(self.vertexList[t], weight=weight)
def getVertices(self):
return self.vertexList.keys()
def __iter__(self):
return iter(self.vertexList.values())
# 3. BFS搜索过程
def BFS(graph: Graph, start):
"""
图的宽度遍历算法,采用队列不断加入新发现的节点
"""
quene = Quene()
startVertex = graph.getVertex(start)
quene.enquene(startVertex)
while not quene.isEmpty():
currentVertex = quene.dequene() # type: Vertex
for v in currentVertex.getConnections(): # type: Vertex
if v.getColor() == 'white':
v.setColor('gray')
v.setPredecessor(currentVertex)
v.setDistance(currentVertex.getDistance()+1)
quene.enquene(v)
currentVertex.setColor('black')
return graph