Dijkstra算法用于寻找图中的某个节点到其他节点的最短路径的大小
这里实现的算法只会保存最短路径的大小,而不会记录最短路径所经过的节点
若记录经过的路径需要对算法进行改进,之后会在该文章后继续进行补充
这里先实现最简单的Dijkstra算法
书中介绍Dijkstra算法有两种实现
1.扫描整个包含v个元素的表来搜索最小值(时间复杂度为O(n2))
2.利用优先队列实现Dijkstra算法(对于稀疏图的时间复杂度为ɵ(V+E)logE)
方法一:
void Dijkstra(Graph* G, int* D, int s) { int i, v, w; for (int i = 0; i < G->n(); i++)//将所有节点路径数组初始化为无穷大,以便下面进行操作 { D[i] = LIMIT; } D[0] = 0; //将第一个节点的距离初始化为0,表示从这个点到自己本身路径是0 for (int i = 0; i < G->n(); i++) { v = minVerex(G, D); //我们通过这个函数来找出数组中最小的一个节点,下一个处理它 if (D[v]==LIMIT) //如果数组中连最小的节点的距离都是无穷,那么这个图是无法达到别的顶点的 //因此直接结束dijkstra算法 { return; } G->setMark(v, VISITED); //将找出的最小的v标记 for ( w = G->first(v); w < G->n(); w = G->next(v,w))//这个for循环里,对v的所有子节点进行扫描 { if ( D[w]>D[v]+G->weight(v,w))//如果发现通过v到达的节点的路径小于直接到达的路径,则在数组中更新改位置的值 { D[w] = D[v] + G->weight(v, w); } } } }
这段代码是书上的原代码,但是显而易见有一个问题:
参数列表中的s有什么用?
根据注释,作者应该是想要将s作为单源寻找的起点,但是,代码中并未涉及到s。
因为其中一行代码
D[0] = 0;
这表示将0号顶点到自己的距离设为0,那么这可以表明这个算法是从0号节点开始查找单源最短路径问题的。
那么,如何实现从s顶点开始查找单源最短路径呢?
经过实验,将上面那行代码更改一下即可:
D[s] = 0;
这样以来就实现了从s顶点开始查找。
另外,其中的minVertex函数 遍历数组,找出距离最小的一个节点,从这个距离最小的节点开始找这个节点到其他路径的节点。
下面是更改过后的代码
注:由于c++库中的 INFINITY 表示的是float类型的无穷大,在赋值时可能会出错,因此,我定义了一个宏 LIMIT 表示数 1000000 以此来表示无穷大。
void Dijkstra(Graph* G, int* D, int s)
{
int i, v, w;
for (int i = 0; i < G->n(); i++)//将所有节点路径数组初始化为无穷大,以便下面进行操作
{
D[i] = LIMIT;
}
D[s] = 0;//将第一个节点的距离初始化为0,表示从这个点到自己本身路径是0
for (int i = 0; i < G->n(); i++)
{
v = minVerex(G, D);//我们通过这个函数来找出数组中最小的一个节点,下一个处理它
if (D[v]==LIMIT)//如果数组中连最小的节点的距离都是无穷,那么这个图是无法达到别的顶点的
//因此直接结束dijkstra算法
{
return;
}
G->setMark(v, VISITED);//将找出的最小的v标记
for ( w = G->first(v); w < G->n(); w = G->next(v,w))//这个for循环里,对v的所有子节点进行扫描
{
if ( D[w]>D[v]+G->weight(v,w))//如果发现通过v到达的节点的路径小于直接到达的路径,则在数组中更新改位置的值
{
D[w] = D[v] + G->weight(v, w);
}
}
}
}
int minVerex(Graph* G, int* D)//实际上这里面的两个for循环的本质含义是,查找一个数组中元素的最小值
{
int i = 0, v = -1;
for ( i = 0; i < G->n(); i++)
{
if (G->getMark(i)==UNVISITED)//在所有节点中找到第一个没有被访问的节点然后跳出
{
v = i;
break;
}
}
for ( i++; i < G->n(); i++) //将后面的节点与已取出的一个节点的值进行比较,如果更小,则更新v的值
{
if (G->getMark(i) == UNVISITED && (D[i] < D[v]))
{
v = i;
}
}
return v;
}
按照以下的图来测试,测试从顶点B到各个顶点的最短路径
测试代码:
#include"Graph.h"
int main() {
Graphm A(6); //创建一个6个节点的图
A.setEdge_undirected(0, 2, 2);
A.setEdge_undirected(0, 4, 5);
A.setEdge_undirected(4, 5, 5);
A.setEdge_undirected(2, 3, 2);
A.setEdge_undirected(3, 5, 1);
A.setEdge_undirected(2, 5, 4);
A.setEdge_undirected(5, 1, 5);
A.setEdge_undirected(1, 2, 3);
A.print();
//graphTraverse(A);
int d[6];
Dijkstra(&A, d, 1);
std::cout << std::endl;
for (int i = 0; i < A.n(); i++)
{
std::cout << d[i] << " ";
}
return 0;
}
测试结果:
表示
B->A 最短路径 5
B->B 最短路径 0
B->C 最短路径 3
B->D 最短路径 5
B->E 最短路径 10
B->F 最短路径 5