九度OJ1008-最短路径问题 之 dijkstra算法的理解与实现

因为没学过数据结构,所以不好下手技术分享,但我也知道这是数据结构里面的东西,所以,,,还是先来学学理论吧技术分享。看了也写了测试程序,最后总结一下这个算法,当然这只是表面一点的东西,更深一点的,后面慢慢说。


Dijkstra算法

针对单源点的最短路径问题,Dijkstra提出了一种按路径长度递增次序产生最短路径的算法,即迪杰斯特拉(Dijkstra)算法,其思想是:从图的给定源点到其它各个顶点之间客观上应存在一条最短路径,在这组最短路径中,按其长度的递增次序,依次求出到不同顶点的最短路径和路径长度。


 简单地说,就是先确定起点,然后搜索余下的点,记录离起点最短的点,然后呢,这个被记录的点就变成了下一次找下一个最近点的“起点”了,当然原来的距离列表就要修改了,因为你起点变了嘛,这里的起点是相对来说的,不是你给出的真正的起点,不断重得这个过程就可以找到所有的最小路径。

因为在最短路径中存在这样的事实:

设起点为 v0,而到达另一个终点 k 的有两种可能:1,v0 直接到 vk

                                                      2,v0先到 vj ,vj 到vk,当然中间可能还有更多的中间点,但原理是一样的

                                                            这样,如果v0-->vj 是最小的(如果 j == k,那就是第一种情况),那么,从                                                                 vj 找到到达vk的最短距离就是v0到vk的最小距离。

这也是这个算法叫按路径长度递增次序产生 最短路径的原因!


算法用到的概念:

1.源点集合S

    上面也说过,起点是相对的(这是个人的理解),对应严老师的书本,这些“起点”就组成了源点集合S,所有的点的集合为V。如此一来,每查找一次就会产生一个源点,这些源点要区别于V-S的点(就是还没有被添加到最短径中的点,在这里就是没标志的点,下面解说)


2.始终距离列表 dist[ j ]: 这个数组表示:从源点V0(这个指的是真正的源点)出发,直接到达各个顶点(终点)的距离,如果用邻接矩阵来初始化,那么就类似如下:

    dist[ j ] = adj[ v0 ][ j ];


3.源点标记数组 flag[ i ]: 用来记录哪些点已经是“起点”了,这样就不用再查找这些点了,因为这些点间的最短路径已经知道了


4.路径表 pre[ j ]: 记录路径(这里只讨论单路径的情况,即到达同一个终点的最短路径只有一种可能的情况,多种可能的后面再说,实现起来也差不多),这个数组表示:

                              如 pre[ j ] = m,表示,到达点 j 前要先到达点 m


好了,各个数据结构已经说明。下面是算法流程:

(1)给出起点 v0, 初始化 dist[],flag[],pre[],三个数组,此时源点集合元素为   S = { v0 };

(2)在 V - S集合中查找离“起点”最近的点vi,并把它放进S中,此时 S = { v0, vi};

          这里为什么说是“起点”,因为这个vi就是下一次循环中的起点,为什么这样说,看下面就知道。

  (3)修改始终距离列表dist[j],这个列表如何修改?按下面的原则修改:

           遍历每个没有标志的顶点 k ,如果    dist[ vi ] + adj[vi][k] < dist[k],那么就 dist[k] = dist[ vi ] + adj[vi][k] ,这个式子很好理解,不多说。

          满足上面条件时,修改路径表:pre[ k ] = vi;就是要到达k,先到达vi

   (4)重复(2)(3)共n-1次,n 是顶点数。


算法代码:

用到的一个自定义结构体:

typedef struct Graph
{
	unsigned int vexnum;//记录顶点数,对于邻接矩阵,一定是一个方阵,所以行列相同
	unsigned char *vexs;//记录顶点信息
	int **adj;          //下面注意二维数组的动态生成;
}Graph;


/************************************************************************
* name :       search_short_path_DIJ
* function:    用Dijkstra算法计算无向网G从始点v0到其余各顶点v的最短路径P[v]和其带权长度dist[v]
* parameters:  1 G   图,成员adj邻接矩阵
*              2 v0  指定起点
*              3 dist 始终距离列表
*              4 flag 源集标志列表
*              5 pre 路径表
* return :
************************************************************************/
void search_short_path_DIJ(Graph G, int v0, unsigned int dist[], BOOL flag[], int pre[])
{
	int i,j,k,min,index;

	       // 1 初始化各个数组
	for (i = 0; i < G.vexnum; ++i)
	{
		pre[i]  = v0;
		flag[i] = false;
		dist[i] = G.adj[v0][i];
	}
	dist[v0] = 0;
	flag[v0] = true;//设置S = { v0 }
	       // 2
	for (i = 0; i < G.vexnum - 1; ++i)
	{
		min = INFINITE;
		index = 0;
		for (j = 0; j < G.vexnum; ++j)
		{
			
			if (!flag[j] && dist[j] < min)
			{
				min = dist[j];//记录从v到各个顶点中的最小值
				index = j;    //记录当前最小距离顶点
			}
		}//找到V-S中 dist最小的值的点,下面更新距离列表和路径表

		flag[index] = true;//把上面找到最小点加到S集合中
		// 3
		for (k = 0; k < G.vexnum; ++k)
		{
                     if (!flag[k] && (dist[index] + G.adj[index][k]) < dist[k])//这里变更了起点,变成index,因为从v出发的最短距离是先到index,注意这里查找                                                                               //的点是V-S中的点
			 {
			     dist[k] = dist[index] + G.adj[index][k];
		             pre[k] = index;//即到达k前,先到达index          
			 }
		}

	}//找到最短路径

}

下面是一个打印路径和返最小短路径的递归函数,因为pre中的点是倒过来找的,所以, 可以用栈去实现路径的打印,当然,可以用递归,在这里,个人用递归来做,因为我不想写一个栈了。。。。。。

打印程序:

/************************************************************************
* name :     print_path_dist
* function:  递归打印最从v0到VX的最短路径,并返回最短距离
* parameter:   1 G   图,成员adj邻接矩阵
*              2 v0  指定起点
*              3 dist 始终距离列表
*              4 vx 终点
*              5 pre 路径表
* return :  distence
************************************************************************/
unsigned int print_path_dist(int v0, int vx, Graph graph, unsigned dist[], int pre[])
{
    unsigned int distance = 0;

	if (v0 != vx)
	{
		distance =  print_path_dist(v0,pre[vx], graph, dist, pre);
		printf("->%d",vx);
		return graph.adj[ pre[vx] ][vx] + distance;
	}
			
	else
	{
		printf("%d",v0);
		return 0;
	}
	
}


测试

下面来测试一下这个程序的效果

1.首先是上个路径图和邻接矩阵(这是一个有向图,对于无向图也是一样的,只不过无向图是一个对称矩阵)


技术分享


2.启动程序,结果如下:

   

技术分享

3,好了, 没问题


后面会改进一下这个处理,使它可以输出多个最短路径的情况。好好学习,天天向上!哈哈哈哈哈




郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。