我学习Dijkstra 算法的过程

来源:互联网 发布:什么软件看爱奇艺会员 编辑:程序博客网 时间:2024/06/06 02:18

网上有看到 Dijkstra算法和 动态规划啦,贪心啦什么的有关。

一开始一头雾水的,怎么就和动态规划有关? 哪来的最优子问题结构? 

后来经过手动跟一遍 Dijkstra过程,突然醒悟到:

 如果 P = A1 A2 ... An 是最A1 到An的最短路, 那么 P 包含了其中任意两点之间的最短路。

否则如果P的两点 Ai Aj 之间存在更短的路, 那么总可以替换掉,从而令P更短。

因此如果P是最短路,那么必定是由各段的最短路组成的。 这就是动态规划的基础了


一个点当然能确定最短路,
两个点最短路也是显然的,
当从两点变到三点的时候, 要么是源点直达最新的点,要么源点经过某些中间点再到达最新点 。
而到达这些中间点的最短路是已经计算好了的,拿来就用。//动态规划嘛,子问题的结果已经算好了,直接用。

一共有n个点,那么怎么从两个点慢慢拓展到全部点呢?
也就是怎么选点的问题,
dijkstra选择了贪心策略,从两点变三点的时候,选的是最小的那个点。
这里需要区分两种点(一般称为 closet-set 和open-set)
close-set中的是已经找到最短路的点(已计算好的子问题的解)       open-set是待计算的。
拓展的过程就是从openset中找一个点加入close-set,直到目标点选入close-set为止

上面有提到找最小的点。
 这点怎么做到? 每计算出一个点的最短路,就尝试用这新最短点作为跳板, 源点经过这最短点之后,到达那些待计算的点,会不会获得更优的距离。
因此随着close-set中的点越来越多,openset中的点的距离也会越来越优。
而其中最小的点,就是下一个最短路的点了。

注意不需要更新那些closet中的点,因为那些已经是最短的了。



正确性的证明用归纳法+ 反证法

算法过程:

       维护两个点集合 S,U.     S并U就是整个图的点。S 是已经找到最短路的点,U是还待计算的。

       每次从U中选择一个点P进入S。 

       那么P怎么选呢? 当前的U中,选择距离源点最近的。

       直到U空集,全部点到源点的最短路都计算出来了。


S12345671046∞4∞∞1,2046∞49∞1,2,5046649∞1,2,5,3046649∞1,2,5,3,4046649∞1,2,5,3,4,6046649121,2,5,3,4,6,7046649

12


求出从点1到其他点的最短路。

1、一开始S中只包含点 1,  表格第一行其实是初始化的值,1 的邻接点的权重,非邻接点权重为无穷。

2、从U中选则距离最小的, 也就是点2. 加入到S中。 并且以2为中继点更新U中各点的最短距离。以点6为例,原本到6的距离是无穷大;而经过2到达6的距离是9,比无穷小,因此更新U中点6 的距离。

3、通过点2更新完U的点之后,再从U中取出新的距离最小的点,点5. 再次更新。以点4为例,原本点4不可达,经过5到达4的话,只需要6距离,因此更新。

一直重复直至U空了 / S包含全部点。

算法结束后,最后一行就是各个点的最短路


问题来了:

1、 第二行中,为什么能确定点2就是最短路呢?

       反证法: 如果存在点K, 1-K-2会更加短,那么1-K 必定比1-2更小,但是我们之所以选到点2,就是因为他是1可达的各点中最小的了,所以不存在这样的K。


2、第5行,为什么能确定点6就是最短的呢?

       因为从1到点6 的距离是通过之前的S(1,2,5,3,4)中的点得到的, 如果此时6的距离还不是最小的,那么只可能是1经过U(7)中的某点到达6.

       也就是说,经过1 出发到7会更短,这和6 的选择是矛盾的。



其实这就是数学归纳的过程, 奠基的一步是问题1,  之后每步的选择都保证选出的点是已经算出最短路径的了。

     

const int infinity = 0x6fffffff; //无穷应选择什么数需要看情况,既要防止溢出,又要保证足够大//算法结束后, distance保存各点最短路 void Dijkstra(int source, int n, int distance[], int *adjacency[]) //adjacency非邻接点权重无穷大 {bool found[n]; //found[i] 为true 表示点 i在S中int i,j;for (i=0; i<n; i++){found[i] = false;distance[i] = adjacency[source][i]; }found[source] = true;distance[source] = 0; //initialization finish for (i=0; i<n; i++){int min = infinity;for (j=0; i<n; j++)  //找到 U 集合中最小的点加入到 S中 {if ( found[j] )  continue;if (distance[j] < min){min = distance[j];}}found[j] = true;for (i = 0; i<n; i++){if ( found[i] )  continue;  //S集合中的已经是最短路,无需更新 if ( min + adjacency[j][i] < distance[i]) // 经过新的最短点j 再到达 i,比之前的走法更短就需要更新 {distance[i] = min + adjacency[j][i]; }} }}



0 0
原创粉丝点击