1、BFS:广度优先搜索
广度优先搜索和二叉树的层序遍历是一样的道理,就是按层次结构逐层遍历排序输出结果。广度优先搜索采用队列的思想来实现,先进先出,在python中直接用list实现即可,只要出队的时候用pop(0)即可,代码及代码解释如下:
'''
图用字典的形式存储,字典的每个键为一个节点,每个键对应的数组由该节点的邻节点组成。
graph = {
'a':['b','c'],
'b':['a','c','d'],
'c':['a','b','d','e'],
'd':['b','c','e','f'],
'e':['c','d'],
'f':['d']
}
'''
#graph为无向图,start为开始的节点
def BFS(graph,start):
queue = [] #python里面的数组可以当作队列用,其pop(0)就是输出第一个元素
queue.append(start) #把开始节点放入队列中
seen = set() #用来保存已经看见到的节点,避免重复计算
seen.add(start) #开始节点被标记为看见了的节点
parent = {} #最后得到的路径中每个节点的上一个节点
parent[start] = None #开始节点的父节点为None
while(len(queue)>0):
vertex = queue.pop(0) #弹出第一个键
nodes = graph[vertex] #nodes保存该键的邻节点
for n in nodes: #遍历邻节点
if n not in seen:
queue.append(n) #如果邻节点没看见过,push进队列
seen.add(n) #同时标记为已经看见
parent[n] = vertex #他的父节点为vertex
bfs_seq.append(vertex)
return parent
parent = BFS(graph,'e') # e作为头结点开始搜索得到一个父节点字典
#求最短路径: b -> e
#此处的最短路径指的是经过的路径最少!非距离!
'''
初始化目标节点,以该节点出发,在parent的字典中依次寻找至值为
None为止,因为e节点为头结点,他对应的父节点为None
注意:要求某个节点到某个节点的路径,先假设某个节点为头结点,然后遍历得到一个parent字典,再在字典中找到另一个节点的位置,从该节点出发,到父节点,不断地寻找父节点直到父节点为None停止.
'''
v = 'b'
short_list = []
while v != None:
short_list.append(v)
v = parent[v]
print(short_list)
结果如下:
['b', 'c', 'e']
2、dijkstra最短路径搜索算法,这里是无向图带权值搜索。此处与BFS不同的是不能单纯地考父节点定义路径,还要有节点到父节点的距离作为标记,这样才方便算出最短距离。
此处每个节点的保存都依赖于他距离头结点的距离,我们只保留距离头结点最短的节点,并且还要保存他到头结点的距离。我们用优先队列的思想来做,用到了python里面自带的heapq包来实现优先队列,而这个包里面是用了小根堆进行排序,小的在头,大的在末尾。
dijkstra开始先要定义所有节点到头结点的初始距离,我们设它为无穷大,而pyhton里面是用math.inf来表示无穷大。
首先要用到最短路径搜索算法,肯定图里面也必须要带权值,所以在保存图的时候就要标记上距离。
graph2 = {
'a':{'b':5,'c':1},
'b':{'a':5,'c':2,'d':1},
'c':{'a':1,'b':2,'d':4,'e':8},
'd':{'b':1,'c':4,'e':3,'f':6},
'e':{'c':8,'d':3},
'f':{'d':6}
}
import heapq
import math
#初始化列表里头结点都各个节点的距离为无穷大
def init_distance(graph,s):
distance = {s:0}
for vertex in graph:
if vertex != s:
distance[vertex] = math.inf
return distance
def dijkstra(graph,s):
pqueue = []
heapq.heappush(pqueue,(0,s))
seen = set()
parent = {}
parent[s] = None
distance = init_distance(graph,s) #与BFS的区别在于多了一个distance来保存距离,当然也没那么简单。
while(len(pqueue)>0):
pair = heapq.heappop(pqueue) #拿出来的是一个元组
dist = pair[0] #元组第一个值记录距离
vertex = pair[1] #元组第二个值记录节点
seen.add(s) #当这个点被拿出来了才能当作被看到了
nodes = graph[vertex].keys() #取出每个节点的邻节点
for n in nodes: #遍历邻节点
if n not in seen:
'''
如果邻节点没被看见过,则要判断邻节点与头结点的距离是否比初始的distance字典中保存的距离小,如果小得更新距离,然后同时更新父节点(父节点保存的必须是最短的路径节点)
'''
if dist+graph[vertex][n] < distance[n]:
distance[n] = dist+graph[vertex][n]
parent[n] = vertex
#同时把这个节点和距离信息放入到优先队列中做比较
heapq.heappush(pqueue,(dist+graph[vertex][n],n))
return parent,distance
好了,我们现在得到的就是一个以传入的节点为头结点来遍历到各个节点的最短路径以及最短距离.
我们来举个栗子:找到e到b的最短路径并把距离输出.思路和BFS的一样,先要把e作为头结点,b作为目标节点,同BFS,首先得得到这个parent字典,然后从b出发走到节点e.
parent,distance = dijkstra(graph2,'e')
print(parent)
print(distance)
short_list = []
v = 'b'
print("b距离e最短距离为%d"%distance[v])
while(v!=None):
short_list.append(v)
v = parent[v]
print(short_list)
最后结果如下:
{'e': None, 'c': 'b', 'd': 'e', 'b': 'd', 'f': 'd', 'a': 'c'}
{'e': 0, 'a': 7, 'b': 4, 'c': 6, 'd': 3, 'f': 9}
b距离e最短距离为4
['b', 'd', 'e']
经验证结果成立。