我学习Dijkstra 算法的过程

网上有看到 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空集,全部点到源点的最短路都计算出来了。

《我学习Dijkstra 算法的过程》

S1234567
10464
1,204649
1,2,5046649
1,2,5,3046649
1,2,5,3,4046649
1,2,5,3,4,604664912
1,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]; 
			}
		} 
	}
}

    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/rxq20081235/article/details/58596431
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞