校园导航

校园导航这个课题,分别可以使用了Dijkstra(迪杰斯特拉)算法,Floyd(弗洛伊德)算法和Bellman-Ford算法。这三种算法都是为了解决求两点之间最短路径的问题。

接下来给大家分享我们理解的这三种算法。

一. Dijkstra(迪杰斯特拉)算法

   有人把他规划在贪心模式算法中,也有人把他划在动态规划算法中,但我觉得说法各有千秋。

Dijkstra算法按路径长度递增次序产生最短路径,首先假设V是所有路径顶点的集合,再引入两个集合,分别是

S:已求出最短路径的顶点的集合

T=V-S:尚未确定最短路径的顶点的集合

将T中顶点按最短路径递增的次序加入到S中,依据:可以证明V0到T中顶点Vk的最短路径,或是从V0到Vk的直接路径的权值或是从V0经S中顶点到Vk的路径权值之和。听不懂吧,接下来我们来举个例子。



我们要用一个二维数组来记录各个顶点之间边的关系,我们把不能直接到达的两点之间的距离用∞(一个很大很大的数)来表示



首先要先确定一个点,我们暂且叫它源点,我们接下来求的也是源点到其它各个点的最短路径,在这里我们把1号点设为源点。然后我们还需要一个一维数组dis记录当前情况下源点到其他各个点的最短距离,并不是真的最短距离,我们把它叫做最短距离的“估计值”



抛去1号点到1号点的距离是0外(因为我们可以确定),让我们选出当前情况下距离1号点最近的点,就是2号点。然后我们就可以把1号点到2号点的最短距离从“估计值”变为“确定值”,即1号顶点到2号顶点的最短路程就是当前dis[2]值。为什么呢?你想啊,目前离1号顶点最近的是2号顶点,并且这个图所有的边都是正数,那么肯定不可能通过第三个顶点中转,使得1号顶点到2号顶点的路程进一步缩短了。因为1号顶点到其它顶点的路程肯定没有1号到2号顶点短。

然后我们就来看看刚被我们确定的2号点能直接到哪几个点吧,可以看出能直接到3号点和4号点。先讨论通过2->3这条边能否让1号顶点到3号顶点的路程变短。也就是说现在来比较dis[3]和dis[2]+e[2][3]的大小。其中dis[3]表示1号顶点到3号顶点的路程。dis[2]+e[2][3]中dis[2]表示1号顶点到2号顶点的路程,e[2][3]表示2->3这条边。所以dis[2]+e[2][3]就表示从1号顶点先到2号顶点,再通过2->3这条边,到达3号顶点的路程。我们发现dis[3]=12,dis[2]+e[2][3]=1+9=10,dis[3]>dis[2]+e[2][3],因此dis[3]要更新为10。同理我们的dis[4]也要从∞(一个很大很大的数)被更新为4。

虽然dis[3]和dis[4]都被更新了,但先别急,它们现在仍是“估计值”还不是“确定值”。让我们想一想现在被确定的点有哪些?(一个是1号点)一个是2号点。这些确定的点就不用管了,让我们在那些还是估计值的点中选出当前情况下距离1号点最近的点,



就是4号点。然后我们就可以把1号点到4号点的最短距离从“估计值”变为“确定值”,即1号顶点到4号顶点的最短路程就是当前dis[4]值。

然后我们再来看看刚被我们确定的4号点能直接到哪几个点吧,可以看出能直接到3号点、5号点、6号点。同样用刚才的方法去更新1号点(源点)到这三个点的最短距离即dis数组中的值。



虽然dis[3]、dis[5]和dis[6]都被更新了,但先别急,它们现在仍是“估计值”还不是“确定值”。让我们想一想现在被确定的点有哪些?一个是2号点、一个是4号点。这些确定的点就不用管了,让我们在那些还是估计值的点中选出当前情况下距离1号点最近的点,



就是3号点。然后我们就可以把1号点到3号点的最短距离从“估计值”变为“确定值”,即1号顶点到3号顶点的最短路程就是当前dis[3]值。

.........

.........

直到dis数组中的值都变成确定值。


下面给大家分享一下我们的核心的代码

核心代码



最后,提醒大家,Dijkstar算法不能处理负权边,每次都找一个距源点最近的点dmin,然后将改距离定为这个点到源点的最短距离,但如果存在负权边,那有可能先通过并不是距源点最近的一个次优点(dmin’),再通过这个负权边L(L<0),使得路径之和更小。则dmin’+L成为最短路径,并不是dmin,这样dijkstra就被pass掉了。


二. Floyd算法

   基本思想是,两点的最短路径不外乎有两种可能,一种是直接最短,第二种是经过若干点最短。所以,我们假设dist(AB)为节点A到节点B的最短路径的距离,对于每个节点K,我们检查dist(Ak)+dist(KB)

代码只有短短几行:

for (intk=0; k

  for (inti=0; i

    for (intj=0; j

      if(dist[i][k] + dist[k][j] < dist[i][j] ) {

        dist[i][j] = dist[i][k] + dist[k][j];

      }

    }

  }

}

最后,注意的是,我们要注意循环的嵌套顺序,如果把把检查所有节点的K放在内层,那么结果就是不正确的。因为这样就提前把最短路径确定下来了,当后面存在最短路径时也不会再更新了。

三.Bellman-Ford算法

Bellman-ford算法是求含负权图的单源最短路径算法,效率很低,但代码很容易写。即进行不停地松弛,每次松弛把每条边都更新一下,若n-1次松弛后还能更新,则说明图中有负环,无法得出结果,否则就成功完成。Bellman-ford算法有一个小优化:每次松弛先设一个flag,初值为FALSE,若有边更新则赋值为TRUE,最终如果还是FALSE则直接成功退出。

dist 1 [u] =

Edge[v][u]


dist k [u] = min{ dist k-1 [u], min{ dist k-1 [j] + Edge[j][u] } },

j=0,1,…,n-1,j≠u

Dijkstra算法和Bellman算法思想有很大的区别:Dijkstra算法在求解过程中,源点到集合S内各顶点的最短路径一旦求出,则之后不变了,修改  的仅仅是源点到T集合中各顶点的最短路径长度。Bellman算法在求解过程中,每次循环都要修改所有顶点的dist[ ],也就是说源点到各顶点最短路径长度一直要到Bellman算法结束才确定下来。

猜你喜欢

转载自blog.csdn.net/abc_soul/article/details/80536407