Dijkstra算法是求解图中单源点最短路径的一种算法,其实现过程类似于Prim算法。顾名思义,“单源点最短路”说明实现功能需要指定源点,并且该算法仅支持各边权值为正数的情况!
基本知识
路径第一个点称“源点”,最后一个点称”终点“。
最短路径指的是两个顶点间经历边数最少的路径。
基本思路
将顶点集合V分成两个集合,一类是生长点的集合S(包括源点和已经确定最短路径的顶点);另一类是非生长点的集合 V-S(包括所有尚未确定最短路径的顶点),并使用一个待定路径表,存储当前从源点V到每个非生长点的最短路径。
存储结构
邻接矩阵存储图,dis数组存储当前最短路径长度,待定路径表path存储表示路径的顶点序列
加入集合用dis数组对应顶点位置置0来表示。
功能实现
与之前的各种算法类似,最短路径算法针对的是图,因此需要建图才可使用该算法。仍然使用C++面向对象的思想:
class MGraph
{
public:
MGraph(DataType a[ ], int n, int e); //构造函数,建n个顶点e条边的图
~MGraph( ){ }; //析构函数
void Dijkstra(int v);
private:
int Min(int r[ ], int n);
char vertex[MaxSize]; //存放图中顶点
int edge[MaxSize][MaxSize]; //邻接矩阵
int vertexNum, edgeNum; //图的顶点数和边数
};
构造函数
与前面的算法类似,需要将图的顶点、边的信息存入类中。
MGraph :: MGraph(char a[ ], int n, int e)
{
int i, j, k, w;
vertexNum = n; edgeNum = e;
for (i = 0; i < vertexNum; i++) //存储顶点
vertex[i] = a[i];
for (i = 0; i < vertexNum; i++) //初始化邻接矩阵,存储各边的权值
for (j = 0; j < vertexNum; j++)
if (i == j)
edge[i][j] = 0;
else
edge[i][j] = 1000; //假设权值最大是1000
for (k = 0; k < edgeNum; k++) //依次输入每一条边
{
cout << "输入边依附的两个顶点的编号,以及边上的权值:";
cin >> i >> j >> w; //输入边依附的两个顶点的编号
edge[i][j] = w; //输入权值
}
}
核心部分
思路
如基本思路所言,初始时,S只包含源点v,对D∈V-S,待定路径表path为从源点v到D的有向边。最短路径数组dis为源点v到D的路径长度。
之后在待定路径表中找到当前最短路径并记录顶点,将新找到的最短路径的顶点加入集合S中。
对不在集合S中(D∈V-S)的点d (d∈D),将路径表中v直接到d的路径长度与经过已确定的最短路径顶点之后再到达d的路径长度相比较,取较小者为v~d的最短路径。
简言之——初始化时,源点到其他各点的dis数组的数据为邻接矩阵源点行的数据,路径path为源点依次到其余点的顶点序列。之后,更新dis数组和path数组的数据。
代码实现
void MGraph :: Dijkstra(int v) //从v出发
{
int i, k, num, dist[MaxSize];
string path[MaxSize];
for (i = 0; i < vertexNum; i++) //初始化数组dis和path
{
dis[i] = edge[v][i];
if (dis[i] != 1000) //假设1000为边上权的最大值
path[i] = vertex[v] + vertex[i]; //字符串连接
else path[i] = "";
}
for (num = 1; num < vertexNum; num++)
//遍历剩余(n-1)个顶点
{
k = Min(dis, vertexNum); //在dis数组中找最小值并返回其下标
cout << path[k] <<":" << dis[k] << endl;
for (i = 0; i < vertexNum; i++) //修改数组dis和path数据
if (dis[i] > dis[k] + edge[k][i]) {
dis[i] = dis[k] + edge[k][i];
path[i] = path[k] + vertex[i]; //字符串连接
}
dis[k] = 0; //顶点k加到集合S中
}
}
其中,找数组最小值元素位置的函数与Prim算法中找最小值位置函数类似,均返回最小值元素的下标:
int MGraph ::Min(int r[ ], int n)
{
int index = 0, min = 1000;
for (int i = 0; i < n; i++)
if (r[i] != 0 && r[i] < min)
{
min = r[i]; index = i;
}
return index;
}