在网图和非网图中,最短路径的含义是不同的。由于非网图它没有边上的权值,所谓的最短路径,其实就是指两顶点之间经过的边数最少的路径;而对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。显然,我们研究网图更有实际意义,就地图来说,距离就是两顶点间的权值之和。而非网图完全可以理解为所有的边的权值都为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)算法解决了从某个源点到其余各顶点的最短路径问题。从循环嵌套可以很容易得到此算法的时间复杂度为,尽管有同学觉得,可不可以只找到从源点到某一个特定终点的最短路径,其实这个问题和求源点到其他所有顶点的最短路径一样复杂,时间复杂度依然是
。
这就好比,你吃了七个包子终于算是吃饱了,就感觉很不划算,前六个包子白吃了,应该直接吃第七个包子,于是你就去寻找可以吃一个就能饱肚子的包子,能够满足你的要求最终结果只能有一个,那就是用七个包子的面粉和馅做的一个大包子。这种只关注结果而忽略过程的思想是非常不可取的。
可如果我们还需要知道如v3到v5、v1到v7这样的任一顶点到其余所有顶点的最短路径怎么办呢?此时简单的办法就是对每个顶点当作源点运行一次迪杰斯特拉(Dijkstra)算法,等于在原有算法的基础上,再来一次循环,此时整个算法的时间复杂度就成了。