Dijkstra算法
Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。注意该算法要求图中不存在负权边。
问题描述:在无向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点 V0 到其余各点的最短路径。(单源最短路径)
算法概要
设置一个图G=(V,E),一个带权无向图。把图中的顶点分为两组,一组为集合S,另一组自然是集合V-S。第一组为已经求得的最短路径的点。(一开始,集合S当中只存在一个源点v0,在不断的求最短路径的途中,集合S不断加入新的点,当集合S=V时,算法结束)。第二组为还未求得的最短路径的点(记为V-S)。
按照最短长度递增的次序不断把集合V-S加入到S集合当中去。(注意:在加入的时候,要保证源点v0到集合S的点的距离要小于或等于v0到集合V-S上的点)。此外,每一个顶点有一个对应的距离,这个距离则是源点v0到该点的最短距离。S中的距离则是v0到该点的最短距离,V-S中的距离则是v0到该点,经过S中的某点作为中转的距离。
代码实现
这是我实现这个方法使用的类
class graph {
private:
int graphSize; //记录图的大小
int **map; //记录图
int *path; //记录最短路径
int *weight; //记录最短路径的长度
//初始化图
void initializeGraph(int v);
//展示图的最短路径
void displayShortestPath(int source);
public:
//初始化一个size*size大的图
graph(int size);
//展示图
void displayGraph();
//寻找最短路径
void findShortestPath(int sourcePoint);
};
这是我定义的一些宏
#define MAX_WEIGHT 100 //设置最大权值
#define CANNOT_CONNECTION -1 //设置路不连通所对应的值
以下是dijkstra算法的代码实现
//Dijkstra算法
void graph::findShortestPath(int sourcePoint)
{
//若输入的源点不合法,则退出算法
if (sourcePoint >= graphSize || sourcePoint < 0) {
cout << "输入不合法" << endl;
return;
}
/*
算法概要
建立顶点集,将顶点分为s,v-s两个顶点集
s顶点集的顶点当中,设为true,v顶点集设为false(属于s集的就是true,不属于的就是false)
从s顶点集开始,不断的向v顶点集扩展,而一开始s集合当中只存在源点sourcePoint
a 计算源点到点i的距离,并记录(i=1,2,3。。。)
b 将与源点距离最短的点u加入到s集合当中
c 计算经过点u到点i的距离(i=1,2,3。。。。)
d 若经过点u到点i的距离小于之前所计算出的距离,重置距离,并将点u设置为前往点i的父节点(i=1,2,3。。。。)
重复b,c,d过程直到集合s包含了所有的点
在计算结束之后,weight[i](我用于记录源点到点i最短距离的数组)里面的数据则是源点sourcePoint到点i的最短距离
而path[i](我用于记录源点到点i的路径的数组)里面的数据则是点i到源点的路径的父节点
想要找到完整的路径只需要不断的从path[i]中寻找父节点,直到找到一个父节点为源点sourcePoint的时候既可以找到完整的路径
*/
bool *s_set = new bool[graphSize];//集合s
/*
算法初始化部分
先将从源点sourcePoint到达其他所有点的距离记录下来
(若该点与源点并无通道,则距离记录为无限大)
将集合s的内容全部置为false,意为还没有开始运算
将从源点sourcePoint到各点的路径记录下来
因为一开始都是从源点到点i,因此路径的父节点全部都是源点sourcePoint
(若点i与源点不相通,则记录为CANNOT_CONNECTION)
*/
for (int i = 0; i < graphSize; i++) {
//记录路程
weight[i] = map[sourcePoint][i];
//重置s集合
s_set[i] = false;
//记录路径
if (weight[i] == MAX_WEIGHT)
path[i] = CANNOT_CONNECTION;
else
path[i] = sourcePoint;
}
//从源点到源点的距离为0
weight[sourcePoint] = 0;
//这个算法是从源点开始计算的
//所以源点一开始就在集合s当中
s_set[sourcePoint] = true;
//计算部分
for (int i = 0; i < graphSize; i++) {
int minWeight = MAX_WEIGHT;//记录最短路(一开始先置为最大)
int u = sourcePoint;//源点为最开始的点
//寻找从源点sourcePoint到不属于s集合的点u的最短距离的点
for (int j = 0; j < graphSize; j++) {
if (!s_set[j] && weight[j] < minWeight) {
//记录这个节点
u = j;
//记录这个最短路程
minWeight = weight[j];
}
}
//将节点u加入到s集合当中
s_set[u] = true;
for (int j = 0; j < graphSize; j++) {
//若点j不属于集合s,并且点u到点j之间存在道路
if (!s_set[j] && map[u][j] != MAX_WEIGHT) {
/*
若通过点u前往点j的路程比直接前往点j的路程要短
(也有可能是通过点u前往到点j的路程比之前计算出的前往点j的路程要短)
则记录这个通向点j的路程
并且将点u记录为前往点j的父节点
*/
if (weight[u] + map[u][j] < weight[j]) {
//记录最短路
weight[j] = weight[u] + map[u][j];
//前往点u的父节点
path[j] = u;
}
}
}
}
//展示路径
displayShortestPath(sourcePoint);
}
展示路径
一些则是展示路径的方法
or (int i = 0; i < graphSize; i++) {
//输出最短距离
cout << "从第" << sourcePoint + 1 << "个点" << "到第" << i + 1 << "个点的最短距离为" << weight[i] << endl;
//输出路径
cout << "路径为:" << i+1 << "<-";
int j = i;
//若路径不是直达的路径
if (path[i] != sourcePoint) {
//不断寻找父节点,直到找到父节点为源点的时候
while (sourcePoint!=j)
{
cout << path[j] + 1;
if (sourcePoint != path[j]) {
cout << "<-";
}
j = path[j];
}//end while
}//end if
else {
//若为直达路径则输出i<-sourcePoint
cout << sourcePoint+1;//因为数组是从0开始的而我的程序的节点的输入是从一开始的,所以输出的时候要补上+1;
}//end else
cout << endl;
}//end for