六、最短路径——迪杰斯特拉(Dijkstra)算法

        在网图和非网图中,最短路径的含义是不同的。由于非网图它没有边上的权值,所谓的最短路径,其实就是指两顶点之间经过的边数最少的路径;而对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。显然,我们研究网图更有实际意义,就地图来说,距离就是两顶点间的权值之和。而非网图完全可以理解为所有的边的权值都为1的网。

        迪杰斯特拉是一个按路径长度递增的次序产生最短路径的算法。它的思路大体是这样的。

        比如说要求下图中顶点v0到顶点v1的最短距离,没有比这更简单的了,答案就是1,路径就是直接vo连线到v1。

         由于顶点v1还与v2、v3、v4连线,所以此时我们同时求得了vo→v1→v2=1+3=4,
v0→v1→v3=1+7=8,v0→v1→v4=1+5=6。
        现在,我问v0到v2的最短距离,如果你不假思索地说是5,那就犯错了。因为边上都有权值,刚才已经有v0→v1→v2的结果是4,比5还要小1个单位,它才是最短距离。

        由于顶点v2还与v4、v5连线,所以此时我们同时求得了v0→v2→v4其实就是v0→v1→v2→v4=4+1=5。这里v0→v2我们用的是刚才计算出来的较小的4。此时我们也发现v0→v1→v2→v4=5要比vo→v1→v4=6还要小。所以vo到v4目前的最小距离是5。

         当我们要求v0到v3的最短距离时,通向v3的三条边,除了v6没有研究过外,

v0→v1→v3的结果是8,而v0→v4→v3=5+2=7。因此,v0到v3的最短距离是7。

        好了,我想你大致明白,这个迪杰斯特拉(Dijkstra)算法是如何干活的了。它并不是一下子就求出了v0到v8的最短路径,而是一步步求出它们之间顶点的最短路径,过程中都是基于已经求出的最短路径的基础上,求得更远顶点的最短路径,最终得到你要的结果。

        现有如下一个网,和它的邻接矩阵

        现在我们看一下如何求出v0到v8的最短路径

#include<iostream>
#include<vector>
#include<queue>
using namespace std;

#define MAXVEX 100//最大顶点数
typedef char VertexType;//顶点类型
typedef int EdgeType;//边上的权值类型
typedef struct
{
	VertexType vexs[MAXVEX];//顶点表
	EdgeType arc[MAXVEX][MAXVEX];//邻接矩阵
	int numVertexte;//当前顶点数
	int numEdges;//当前边数
}MGraph;

void ShortestPath_Dijkstra(MGraph G, int v0, vector<int>& P, vector<int>& D)
{
	//P[i]的值为顶点i的前驱顶点
	//D[i]的值为v0到i的最短路径长度和
	vector<int> final(G.numVertexte);//final[i]=1表示已求得顶点v0到顶点i的最短路径
	for (int i = 0; i < G.numVertexte; ++i)//初始化数据
	{
		final[i] = 0;//全部初始化为未知最短路径状态
		P[i] = 0;
		D[i] = G.arc[v0][i];
	}
	D[v0] = 0;//v0至v0的路径为0
	final[v0] = 1;//v0至v0不需要求路径

	/*开始主循环,每次求得v0到某个顶点的最短路径*/
	for (int i = 1; i < G.numVertexte; ++i)
	{
		int min = INT_MAX;//当前所知离v0顶点的最近距离
		int k;
		for (int w = 0; w < G.numVertexte; ++w)//寻找离v0最近的顶点
		{
			if (!final[w] && D[w] < min)//如果顶点w为加入最短路径,且v0到w的距离小于之前的min
			{
				k = w;
				min = D[w];
			}
		}
		final[k] = 1;//将目前找到的最近的顶点加入最短路径
		for (int w = 0; w < G.numVertexte; ++w)//修正当前最短路径及距离
		{
			/*此时的min就是前面找到的离v0顶点的最近距离,也就是v0~k的距离*/
			if (!final[w] && (min + G.arc[k][w] < D[w]))
			{//如果顶点w还没加入最短路径且从v0->k->w的距离 小于 直接从v0->w的距离
				D[w] = min + G.arc[k][w];//那么就更新v0->w的距离为 v0->k->w的距离
				P[w] = k;//那么顶点w的前驱顶点就是k
			}
		}
	}
}

        final数组是为了v0到某顶点是否已经求得最短路径的标记,如果v0到vi已经有结果,则final[i]=1。

最终的结果

        其实最终返回的数组D和数组P,是可以得到v0到任意一个顶点的最短路径和路径长度的。例如v0到v8的最短路径并没有经过v5,但我们已经知道v0到v5的最短路径了。由D[5]=8可知它的路径长度为8,由P[5]=4可知v5的前驱顶点是v4,所以v0到v5的最短路径是V0→V1→V2→V4→V5。

        也就是说,我们通过迪杰斯特拉(Dijkstra)算法解决了从某个源点到其余各顶点的最短路径问题。从循环嵌套可以很容易得到此算法的时间复杂度为O(n^{2}),尽管有同学觉得,可不可以只找到从源点到某一个特定终点的最短路径,其实这个问题和求源点到其他所有顶点的最短路径一样复杂,时间复杂度依然是O(n^{2})

        这就好比,你吃了七个包子终于算是吃饱了,就感觉很不划算,前六个包子白吃了,应该直接吃第七个包子,于是你就去寻找可以吃一个就能饱肚子的包子,能够满足你的要求最终结果只能有一个,那就是用七个包子的面粉和馅做的一个大包子。这种只关注结果而忽略过程的思想是非常不可取的。 

        可如果我们还需要知道如v3到v5、v1到v7这样的任一顶点到其余所有顶点的最短路径怎么办呢?此时简单的办法就是对每个顶点当作源点运行一次迪杰斯特拉(Dijkstra)算法,等于在原有算法的基础上,再来一次循环,此时整个算法的时间复杂度就成了O(n^{3})

  • 20
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值