指定一个点(源点)到其余各个顶点的最短路径,也叫做“单源最短路径”。例如求下图中的1号顶点到2、3、4、5、6号顶点的最短路径。

                                                                  

首先用二维数组来存储这个图, 如下:

                                                             

我们还需要用一个一维数组dis来存储1号顶点到其余各个顶点的初始路程,如下。

                                                                  

比如我们设俩个集合 S  ,  U, 分别代表确定值和估计值

那么一开始:

S = {1 };

U = {2,3 ,4,5,6};

先找一个离1号顶点最近的顶点,是2号顶点,当选择了2号顶点后,dis[2]的值就已经 从“估计值”变为了“确定值”。

S= {1, 2};

U = {3 , 4 , 5 ,6 };

接下来看2号顶点的出边,有2-3和2-4两条边。先讨论通过2-3能否让1-3号的路程变短,也就是比较dis[3]和dis[2]+e[2][3]的大小。(松弛操作)。

松弛操作其实就是指通过另外一个点作为中转点,更新这俩点之间的距离。

最终结果:S= {1 ,2 ,4 ,3, 5 ,6} // 最短路径

U = {};


  既然是求1号顶点到其余各个顶点的最短路程,那就先找一个离1号顶点最近的顶点。通过数组dis可知当前离1号顶点最近是2号顶点。当选择了2号顶点后,dis[2]的值就已经从“估计值”变为了“确定值”,即1号顶点到2号顶点的最短路程就是当前dis[2]值。为什么呢?你想啊,目前离1号顶点最近的是2号顶点,并且这个图所有的边都是正数,那么肯定不可能通过第三个顶点中转,使得1号顶点到2号顶点的路程进一步缩短了。因为1号顶点到其它顶点的路程肯定没有1号到2号顶点短,对吧O(∩_∩)O~

 

        既然选了2号顶点,接下来再来看2号顶点有哪些出边呢。有2->3和2->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。这个过程有个专业术语叫做“松弛”。即1号顶点到3号顶点的路程即dis[3],通过2->3这条边松弛成功。这便是Dijkstra算法的主要思想:通过“边”来松弛1号顶点到其余各个顶点的路程。

 

        同理通过2->4(e[2][4]),可以将dis[4]的值从∞松弛为4(dis[4]初始为∞,dis[2]+e[2][4]=1+3=4,dis[4]>dis[2]+e[2][4],因此dis[4]要更新为4)。

 

        刚才我们对2号顶点所有的出边进行了松弛。松弛完毕之后dis数组为:
 

 

        接下来,继续在剩下的3、4、5和6号顶点中,选出离1号顶点最近的顶点。通过上面更新过dis数组,当前离1号顶点最近是4号顶点。此时,dis[4]的值已经从“估计值”变为了“确定值”。下面继续对4号顶点的所有出边(4->3,4->5和4->6)用刚才的方法进行松弛。松弛完毕之后dis数组为:
 

 

        继续在剩下的3、5和6号顶点中,选出离1号顶点最近的顶点,这次选择3号顶点。此时,dis[3]的值已经从“估计值”变为了“确定值”。对3号顶点的所有出边(3->5)进行松弛。松弛完毕之后dis数组为:
 

 

        继续在剩下的5和6号顶点中,选出离1号顶点最近的顶点,这次选择5号顶点。此时,dis[5]的值已经从“估计值”变为了“确定值”。对5号顶点的所有出边(5->4)进行松弛。松弛完毕之后dis数组为:
 

 

        最后对6号顶点所有点出边进行松弛。因为这个例子中6号顶点没有出边,因此不用处理。到此,dis数组中所有的值都已经从“估计值”变为了“确定值”。

 

        最终dis数组如下,这便是1号顶点到其余各个顶点的最短路径。
 

下面代码(超级详细):

#include <stdio.h>
int main()
{
	int dis[10] ;//存储源点到其他顶点之间的最短距离
	int e[10][10] ;//存图存边
	int book[10];//标记确定的值
	int n , m ,t1 , t2 , t3, u , min;
	int INF = 999999;//无穷值 
	scanf("%d %d", &n, &m);//n表示顶点数, m表示边的条数
	
	//初始化
	for(int i = 1 ; i <= n ; i++)
	{
		for(int j = 1 ; j <= n ; j++)
		{
			if(i == j)
			{
				e[i][j] = 0;
			}
			else
			{
				e[i][j] = INF;
			}
		}
	 } 
	 //读入边
	 for(int i =1 ; i <= m ; i++)
	 {
	 	scanf("%d %d %d" , &t1 ,&t2 , &t3);
	 	e[t1][t2] = t3;
	  } 
	  //初始化dis数组 , 源点到其他顶点的初始路程 ,可直接到达就直接写该距离 , 不能就初始为INF
	  for(int i = 1 ; i <= n ; i++)
	  {
	  	dis[i] = e[1][i];
	   } 
	   //初始化book数组
	   for(int i = 1; i <= n ;i++)
	   {
	   	book[i] = 0;
	   	book[1] = 1;
		} 
		
	//当当当~~~~ 重点来了 , 迪杰斯特拉算法核心语句
	for(int i = 1 ; i <= n-1 ; i++)//循环dis数组  , 找最近的点 
	{
		min = INF;
		for(int j = 1 ; j <= n ; j++)//
		{
			if(book[j] == 0 && dis[j] < min)
			{
				min = dis[j];//确定最短路径通过的顶点 
				u = j;
			}
		 } 
		 book[u] = 1;//该点确定 
		 for(int v = 1 ; v <= n ; v++)//松弛操作 
		 {
		 	if(e[u][v] < INF)
		 	{
		 		if(dis[v] >dis[u] +e[u][v])
		 		{
		 			dis[v] = dis[u] + e[u][v];
				 }
			 }
		  } 
	 } 
	  for(int i = 1 ; i <= n ; i++)
		  {
		  	printf("%d " , dis[i]);
		   } 
	return 0;
 } 
 
 /*
 6 9
 1 2 1
 1 3 12
 2 3 9
 2 4 3
 3 5 5 
 4 3 4
 4 5 13
 4 6 15
 5 6 4
 */