最短路径算法之一:单源无权图,python实现

在图论中,有一类经典问题,就是最短路径问题。最短路径问题,又分为:

  • 单源无权图最短路径问题
  • 单源有权图最短路径问题
  • 多源有权图最短路径问题

问题:啥叫单源,多源?

单源指的是给定一个起点,求到任意顶点的最短路径;多源指的是给定两个顶点,求这两个顶点的最短路径。

问题:啥叫有权,无权?

权,即权重,指的是两个邻接点的边的权重。无权的条件下,我们默认所有边的权重一致(给个默认值);有权条件下,每条边的权重不一致。这就导致了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,访问V1的所有邻接点。

  • V1的邻接点有V2、V4。V2、V4都没有访问过,更新他们的dist为dist[V1] + 1,并依次入队
  • 此时队列 [ v2, v4 ]

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

  • V2的邻接结点有V4、V5,由于V4已经访问过了,跳过V4。更新dist[V5] = dist[V2]+1,V5入队。
  • 此时队列 [ v4, V5 ]

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

  • V4的邻接结点有V3、V5、V6、V7,由于dist[V5]不等于-1,表示已经访问过了,跳过V5。更新dist[V3/6/7] = dist[V4]+1,V3、V6、V7入队。
  • 此时队列 [v5, v3, v6, v7]

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

  • V5的邻接结点有V7,但是由于V7已经访问过了,所以跳过
  • 此时队列 [v3, v6, v7]

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

  • V3的邻接结点有V1、V6,但是由于V1和V6都访问过了,所以跳过
  • 此时队列 [v6, v7]

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

  • V6的没有邻接结点,跳过
  • 此时队列 [v7]

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

  • V7的邻接结点有V6,由于V6已经访问过了,跳过
  • 此时队列 [],为空

此时判断队列为空,说明所有结点已经求得最短路径。

代码实现:

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)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值