1. 解决问题
目的是求某一顶点到其余各个顶点【最短路径】。
2. Dijkstra算法思想
假设一共有两个顶点集合 S 和 T ,集合 S 中存放图中已找到最短路径的顶点,集合 T 中存放图中的剩余顶点。
初始状态时,S 中只包含源点 V0,然后不断通过从集合 T 中选取到顶点 V0 路径长度最短的顶点 Vu 并入集合 S 中。
集合 S 每并入一个新的顶点Vu ,都要修改顶点 V0 到集合 T 中顶点的最短路径长度值。
不断重复这个过程,直到所有顶点全部并入集合 S 中为止。
3. 辅助数据结构
Set T[ ]:已找到最短路径的结点集合。这里为了简单起见用 int T[ ] 表示,当 T[i]==1 表示存在上述集合 S 中,T[i]==0 表示存在上述集合 T 中。
int pre[ ]:记录V0 到 Vi 的前驱结点 Vi-1。
int dist[ ]:记录V0 到 Vi 的最短路径的长度。
4. 图的表示
这里采用简单的邻接矩阵表示有向图。
int graph[7][7] = {
{0, 4, 6, 6, 0, 0, 0},
{0, 0, 1, 0, 7, 0, 0},
{0, 0, 0, 0, 6, 4, 0},
{0, 0, 2, 0, 0, 5, 0},
{0, 0, 0, 0, 0, 0, 6},
{0, 0, 0, 0, 1, 0, 8},
{0, 0, 0, 0, 0, 0, 0}
};
5. 代码描述
5.1 judge()
判断T中是否包含所有的顶点,如果是Set类型则不需要此函数,只需要 set.find(i) 即可。
bool judge(int n) {
for (int i = 0; i < n; ++i) {
if (T[i] == 0)
return false;
}
return true;
}
5.2 minIndex()
返回 dist[ ] 数组中所有路径距离最小的顶点下标。
int minIndex(int n) {
int index = -1;
int min = INT_MAX;
for (int i = 0; i < n; i++) {
if (T[i] == 0 && dist[i] < min) {
min = dist[i];
index = i;
}
}
return index;
}
5.3 shortestPath()
第一步:直接可达的顶点,用距离来初始化dist,dist[start]=0,可直达的把距离记录下来作为待定值。
第二步:从待定的距离表中找到最小值min以及对应的顶点 M ,这个值可以作为确定值,为什么?(因为不可能会有图中任何一个顶点 x ,由 start 经过 x 再到顶点 M 产生的距离,要比直接从 start 到 M 更小)
第三步:看这个新确定的顶点的出度,看看从源点出发是经过这个顶点到其邻居近还是直达更近,如果更近就要更新。
void shortestPath(int start, int n) {
pre[start] = start;
dist[start] = 0;
T[start] = 1;
//step1
for (int i = 0; i < n; i++) {
if (i != start) {
if (graph[start][i] == 0)
dist[i] = INT_MAX;
else if (graph[start][i] > 0) {
dist[i] = graph[start][i];
pre[i] = start;
}
}
}
while (judge(n) == false) {
//step2
int index = minIndex(n);
T[index] = 1;
if (judge(n) == true)
break;
//step3
for (int neighbor = 0; neighbor < n; neighbor++) {
int weight = graph[index][neighbor];
if (weight > 0 && (dist[index] + weight) < dist[neighbor]) {
dist[neighbor] = dist[index] + weight;
pre[neighbor] = index;
}
}
}
}
5.4 main()
应该倒着输出 pre[ ] ,因为存的是前驱结点,也可以用栈处理一下。
int main() {
int n = sizeof(graph[0]) / sizeof(int);
shortestPath(0, n);
for (int i = 0; i < n; ++i) {
cout << dist[i] << " ";
}
cout << endl;
for (int i = 0; i < n; ++i) {
cout << pre[i] << " ";
}
}
6.总结
Dijkstra算法是图论中经典的求最短路径的算法,笔者在学习过程中发现它运用的是动态规划的思想,此外图论还有拓扑排序,Prim、Kruskal求最小生成树算法、求任意两点间的最短路径的Floyd算法,以及求关键路径的算法,这些对于考研和工作来说都是必须掌握的数据结构算法的基本知识,希望与大家共勉!