图的最短路径学习笔记

   对一个连通图,我们可以构造一个每条边到实数R的映射w,则一条路径(e1,e2,...,en)的长度可以表示为l=w(e1)+w(e2)+...+w(en)。图中两点间可能存在多条路经,而在这些路径中长度最短的为这两点间的距离。而图的最短路径问题便是寻找这个距离,以及输出得到这个距离的路径。
(1)从一个固定顶点到其他顶点的距离
a.当每条边的权值为1的时候,可以用BFS在访问每个结点时设定一定操作来解决,dist数组记录每个结点的到s点距离,prev记录到达该结点之前的一个结点值
Procedure BFS(G,s):
  for all u in V:
    dist(u)=inf
    prev(u)=NULL
  dist(s)=0
  prev(s)=NULL
  queue Q={s}
  while Q is not empty:
    u=Q.pop()
    for all edges (u,v) in E:
      if dist(v)=inf:
        Q.push(v)
        dist(v)=dist(u)+1
        prev(v)=u
  该算法的复杂度为O(E),因为每条边最多访问2次
b.当每条边的权值不都为1,且没有负边存在的时候,可以用Dijkstra算法来解决
Procedure Dijkstra(G,s):
  for all u in V:
    dist(u)=inf
    prev(u)=NULL
  dist(s)=0
  prev(s)=NULL
  U=V
  while U!=empty:
    find some u in U such that dist(u) is minimal
    U=U-{u}
    for all edges (u,v) in E:
      if dist(v)>dist(u)+l(u,v):
        dist(v)=dist(u)+l(u,v)
        prev(v)=u
  该算法的复杂度为O(|V|*|V|),因为每个顶点都会被访问到,而且访问每个顶点的时候都要线性查找出当时队列里面的最小值。
  可以通过使用适当的数据结构来减少复杂度,可以使用priority_queue,这样每次查找只需要常数时间,而当结点变化的时候需要logV时间来调整队列。
Procedure Dijkstra(G,s):
  for all u in V:
    dist(u)=inf
    prev(u)=NULL
  dist(s)=0
  prev(s)=NULL
  U=priority_queue(V)
  while U!=empty:
    u=U.pop()
    for all edges (u,v) in E:
      if dist(v)>dist(u)+l(u,v):
        dist(v)=dist(u)+l(u,v)
                    prev(v)=u
  该算法的复杂度为O(|V|*log|V|)。
  Dijkstra算法与BFS的区别主要是改变了出队列的顺序。
c.当存在有权值为负数的边时,Dijkstra算法不正确。即使使每条边加上一定的常数,使得图没有负数边存在,得到的最短路经可能也是有问题的。
  当每条边的权值不都为1,且有的边权值为负,但不存在为长度为负的回路时,可以用Bellman_Ford算法来解决,算法思路如下:因为最短路径最多只包含|v|-1 个点,所以,只需要循环|v|-1 次。
  算法返回改图是否有长度为负的回路,最短距离保存在数组dist里面。
Procedure Bellman_Ford(G,s):
  for all u in V:
    dist(u)=inf
    prev(u)=NULL
  dist(s)=0
  prev(s)=NULL
  repeat |V|-1 times:
    for all e(u,v) in E:
        if dist(u)>dist(v)+l(u,v):
          dist(u)=dist(v)+l(u,v)
        if there doesn't exit any vertex such that dist(u)>dist(v)+l(u,v):
          return
  算法复杂度为O(|V||E|)
  还可以用SPFA算法来解决,该算法的思路是:初始时将起始结点加入队列。 每次从队列中取出一个元素,并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,且该点不在队列中,则将其入队。 直到队列为空时算法结束。
Procedure SPFA(G,S):
 for all u in V:
   dist(u)=inf
   prev(u)=NULL
 dist(s)=0
 prev(s)=NULL
  queue Q
  Q.push(s)
  while Q is not empty:
    u=Q.pop()
    for all edge (u,v) in E:
      if dist(v)>dist(u)+l(u,v):
          dist(v)=dist(u)+l(u,v)
          prev(v)=u
          if v is not in Q:
            Q.push(v)
  该算法的复杂度是O(k|E|)。
(2)图中任意两点的距离,即得到一个距离矩阵
a.可以每次使用一个不同结点作为源结点来解决,但这种算法的复杂度太大,所以一般不才用
b.用Floyd-Warshall算法,算法思路是动态规划,V={1,2,3...n},dist(i,j,k)表示从i到j,中间只经过V中前k个结点的最短路经,而i到j的最短路经就应该为dist(i,j,n)
  由动态规划可知:
  dist(i,j,k)=min{dist(i,j,k-1),dist(i,k,k-1)+dist(k,j,k-1)}
  又因为k阶值总是由k-1阶值运算而来,所以不需要用三维数组来存,用dist(i,j)加一个循环就好。
Procedure Floyd-Warshall(G):
  for i=1 to n:
   for j=1 to n:
     if i!=j:
       d(i,j)=l(i,j)
     else:
       d(i,j)=0
  for k=1 to n:
    for i=1 to n:
      for j=1 to n:
        dist(i,j)=min{dist(i,j),dist(i,k)+dist(k,j)}
算法复杂度为O(|V|*|V|*|V|)
(3)当图中有负回路的时候,不存在最短路径。有三种不同的方法判断是否存在负回路
a.在Bellman_Ford算法中,如果循环了|V|-1次后还存在 dist(u)>dist(v)+l(u,v)可以松弛的点,则存在负回路
b.在SPFA算法中,如果一个顶点入队列|V|次,则存在负回路
c.在Floyd-Warshall算法中,如果在运算过程中,d(i,i)出现负数,则存在负回路
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值