本文主要讨论带权有向图的两个问题:
- 求某一顶点到其它顶点的最短路径;
- 求任意一对顶点之间的最短路径。
目录
一、Dijkstra算法
1.1 基本思想
迪杰斯特拉(Dijkstra)提出了求解这种问题的方法。
1.2 算法思想
辅助数组dist[ ]:dist[i]表示目前已经找到的从开始点到终点的当前最短路径长度。
辅助数组path[ ]:path[i]表示目前已经找到的从开始点到终点的当前最短路径顶点序列。
1.3 算法实现
#define INFINITY 32768
typedef int WeightType;
typedef SeqList VertexSet; /* 线性表 —— 顶点集 */
Member函数用于判断顶点是否在顶点集S中。
bool Member(VertexData v, VertexSet *S) {
for (int i = 0; i < S->last; ++i)
if (v == S->elem[i])
return true;
return false;
}
Dijkstra算法。
void ShortestPath_DJS(Graph *G, int v0, WeightType dist[], VertexSet path[]) {
/* 初始化辅助数组dist和path */
for (int i = 0; i < G->vexnum; ++i) {
InitList(&path[i]);
dist[i] = G->arcs[v0][i].adjvex;
if (dist[i] < INFINITY) {
/* AddTail是表尾添加操作 */
AddTail(&path[i], G->vertex[v0].data);
AddTail(&path[i], G->vertex[i].data);
}
}
VertexSet S; /* S为已找到最短路径的终点集合 */
InitList(&S);
AddTail(&S, G->vertex[v0].data);
int k, min;
for (int t = 1; t <= G->vexnum - 1; ++t) {
min = INFINITY;
for (k = -1, i = 0; i < G->vexnum; ++i)
if (!Member(G->vertex[i].data, & S) && dist[i] < min) {
k = i;
min = dist[i];
}
/* 未找到合适的终点,直接进入下一趟循环 */
if (k == -1) continue;
/* 将Vk加入到S中 */
AddTail(&S, G->vertex[k].data);
/* 修正dist[i] */
for (int i = 0; i < G->vexnum; ++i)
if (!Member(G->vertex[i].data, &S) && G->arcs[k][i].adjvex
!= INFINITY && (dist[k] + G->arcs[k][i].adjvex < dist[i])) {
dist[i] = dist[k] + G->arcs[k][i].adjvex;
path[i] = path[k];
/* path[i] = path[k]∪{Vi} */
AddTail(&path[i], G->vertex[i].data);
}
}
}
二、Floyd算法
2.1 基本思想
下面给出一个例子。
第一次迭代,加入顶点a:
第二次迭代,加入顶点b:
第三次迭代,加入顶点c:
2.2 算法实现
void ShortestPath_Floyd(AdjMatrix *G,
WeightType dist[][MAX_VERTEX_NUM],
VertexSet path[][MAX_VERTEX_NUM]) {
int i, j, k;
/* 初始化辅助数组dist和path */
for (i = 0; i < G->vexnum; ++i)
for (j = 0; j < G->vexnum; ++j) {
InitList(&path[i][j]);
dist[i][j] = G->arcs[i][j].adjvex;
if (dist[i][j] < INFINITY) {
AddTail(&path[i][j], G->vertex[i].data);
AddTail(&path[i][j], G->vertex[j].data);
}
}
/* 进行n次迭代,找出最短路径 */
for (k = 0; k < G->vexnum; k++)
for (i = 0; i < G->vexnum; i++)
for (j = 0; j < G->vexnum; j++)
if (dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
/* JoinList是合并线性表操作 */
JoinList(&path[i][k], &path[k][j], &path[i][j]);
}
}
三、辅助函数
算法中使用到的辅助函数:
- AddTail
- JoinList
- ListLength
- InitList
bool AddTail(LListPtr L, ElemType e) {
return InsList(L, L->last + 1, e);
}
void JoinList(LListPtr La, LListPtr Lb, LListPtr Lc) {
int i, j;
for (i = 0; i < La->last; ++i)
Lc->elem[i] = La->elem[i];
for (--i, j = 0; j < Lb->last; ++i, ++j)
Lc->elem[i] = Lb->elem[j];
Lc->last = i;
}
int ListLength(LListPtr L) {
return L->last;
}
void InitList(LListPtr L) {
L->last = 0;
}