在图论中,有一类经典问题,就是最短路径问题。最短路径问题,又分为:
问题:啥叫单源,多源?
单源指的是给定一个起点,求到任意顶点的最短路径;多源指的是给定两个顶点,求这两个顶点的最短路径。
问题:啥叫有权,无权?
权,即权重,指的是两个邻接点的边的权重。无权的条件下,我们默认所有边的权重一致(给个默认值);有权条件下,每条边的权重不一致。这就导致了V1到V2也许经过的顶点数不是最少的,但可能是路径最短的,比如下图:

v1-v3经过的顶点最少,但路径不是最短的,最短路径是v1-v2-v3。
这篇文章,讨论单源无权图的最短路径,以下图为例展开讨论。

算法思想:
利用BFS广度优先算法,把路径按照递增的顺序入队,对第k个顶点,假设他的最短路是n,那么对k的(未访问过的)邻接节点来说,它们的最短路则是n+1,所以满足BFS满足路径递增要求。用一个dist 数组保存s到各顶点的最短路径。dist[i] 表示 从顶点s到i 的最短路径,默认为-1。
先把起点s压入队列,dist[s] = 0(自己到自己);
从队列中弹出一个顶点V,直到队列为空;遍历该顶点的所有未经访问的邻接结点(未经访问的结点dist[i] = -1,否则为最短路径),并更新邻接结点的dist为 dist[V]+1;
当队列为空时,已经求得s到任意顶点的最短路径。
步骤拆分分析:
先确认起点s,假设起点是V1,V1入队。
从队列中弹出一个顶点:V1,访问V1的所有邻接点。

从队列中弹出一个顶点:V2,V2的所有邻接结点的最短路径是 dist[V2]的基础上再加一条边,符合按照路径递增的顺序访问,先访问dist = 1的顶点,然后访问dist=2的顶点,.....。

从队列中再弹出一个顶点:V4,接着跟上次的步骤一样。

从队列中再弹出一个顶点:V5,接着跟上次的步骤一样。
从队列中再弹出一个顶点:V3,接着跟上次的步骤一样。
从队列中再弹出一个顶点:V6,接着跟上次的步骤一样。
从队列中再弹出一个顶点:V7,接着跟上次的步骤一样。
此时判断队列为空,说明所有结点已经求得最短路径。
代码实现:
- class UnweightBFS(BFSVisit):
-
- def __init__(self):
- super(UnweightBFS, self).__init__()
- self.dist = []
-
- def initVisted(self, g):
- super(UnweightBFS, self).initVisted(g)
- self.dist = [-1 for v in range(g.nV)] # 保存s到i的最短路径
-
- def visit(self, g, v: int):
- """
- 最短路径
- g: 图(矩阵实现)
- v: 起点
- """
- Q = QueueArr() # 初始化一个空队列 (我自己写的队列)
- Q.addQ(v)
- self.dist[v] = 0
-
- while(not Q.isEmpty()):
- V = Q.deleteQ()
-
- for i, val in enumerate(g.G[V]):
- if (self.dist[i] == -1 and g.G[V][i] > 0): # 未访问过,且是邻接节点(有边)
- self.dist[i] = self.dist[V] + 1
- Q.addQ(i)