Dijstra,求解单源最短路径问题,解决一个顶点到其它所有顶点的最短路径,但是无法求解权值为负数的情况(负权值可考虑使用spfa)。是一种基于BFS(广度优先)从起点开始,一层层向外拓展,逐步更新数据,直到拓展到终点为止。
基本思想
初始有S,U两个集合,S记录已经求出最小值的顶点,U记录还未求出最小值的顶点,dist数组记录顶点到其余所有点的最短路径长度,path数组用来记录最短路径。。
初始时,S中只包含源点u,然后从U中找到离S中的点,最短的一条边,(注意,此时S中的点不一定只有源点,是找S中的点与U中的点随机组合中权值最小的那个边)然后将这条边对应的U中的顶点加入S,并更新与U中顶点相关的路径。重复操作,直到遍历图中所有顶点都加入S中。
操作步骤
(1)初始化:S中只包含源点u,U中包含除源点外的所有点,dist中与u相连的边如<u,v>,dist[v]设置为<u,v>的权值(dist[u] = 0, 其余未连接的顶点的dist设置为INF,即无穷大),path[u] = v(其它未连接的点path均设置为-1)。
(2)从S中选择一个顶点s,U中选择一个顶点p,使得<s,p>是最小的,然后将U中的p加入到S中。
(3)更新S中的点的最短距离,即更新dist, path数组,(注意dist,数组中记录的最短路径长度和path数组中记录的最短路径都是当前条件下的最短路径,并非全局的最短路径,随着U中新顶点的不断加入,不断更新dist,path中的最短路径,当遍历完图中所有顶点,得到的就是源点到其它顶点的最短路径)。
更新方法:因为<s,u>是我们找到的最短的那条边,所以我们只需要判断<u,v>的权值与<u,p> + <p,v>的权值哪个更小,若新加入的p使得源点u到顶点v的距离缩短了,即**(<u,p> + <p,v>) < (<u,v>)**,那么我们就更新dist,path中关于v的数据。
(4)循环重复(2),(3)的操作,直到U中所有的顶点都加入到S中。
话不多说,上代码
代码分析
数据结构
typedef struct ANode {
int adjvex;
struct ANode *nextarc;
int weight;
}ArcNode; //边结点的类型
typedef struct Vnode {
int info;
ArcNode *firstarc;
}VNode; //头节点类型
typedef struct {
VNode adjlist[MAXV];
int n, e;
}AdjGraph; //邻接表
算法代码
void Dijkstra(AdjGraph G, int u, int v) { //图G,起点u, 终点v
int dist[Max];//最短路径长度数组
int path[Max];//最短长度数组,路径数组
int S[Max];//标记数组
int i, j, w;
int min;
ArcNode *p;
for (i = 0; i < G.n; i++) {
S[i] = 0;
dist[i] = INF;
path[i] = -1;
} //初始化三个数组
p = G.adjlist[u].firstarc;
while (p != NULL) {
dist[p->adjvex] = p->weight;
path[p->adjvex] = u;
p = p->nextarc;
}
S[u] = 1; path[u] = 0;
for (i = 0; i < G.n - 1; i++) {
min = INF;
for (j = 0; j < G.n; j++) { // 找最小边<s,p>
if (S[j] == 0 && dist[j] < min) {
w = j;
min = dist[j];
}
}
S[w] = 1;
p = G.adjlist[w].firstarc;
while (p != NULL) {
if (S[p->adjvex] == 0) {
int mindis = (dist[w] + (p->weight));
if (mindis < dist[p->adjvex]) { //更新dist,path数组
dist[p->adjvex] = mindis;
path[p->adjvex] = w;
}
}
p = p->nextarc;
}
Display(u, v
}
输出路径
上面的算法完成后,dist数组中存储了最短路径长度,例如<u,v>的最短路径为2,dist[v] = 2, 但是要想得到最短路径,需要path数组的帮助,path中存储了最短路径的前驱结点,例如还是<u,v>最短路径为u -> a -> b -> c -> v,那么path中依次为path[v] = c,path[c = b,path[b] = a,path[a] = u,path[u] = -1。
void Display(int u, int v, int *path) {
int d = 0;
int path_Dijstra[2][Max];
int cntDijstra; //记录最短路径一共需要经过多少个顶点
path_Dijkstra[0][d] = v;
int k = path[v];
while (k != -1) {
d++; path_Dijkstra[0][d] = k;
k = path[k];
} //依照前驱结点关系,path_Dijkstra[0]中存储的是倒序路径,需要得到其正序
for (i = d; i > 0; i--)
path_Dijkstra[1][d - i] = path_Dijkstra[0][i - 1];//path_Dijkstra[1]记录正序最短路径
cntDijkstra = d + 1;
}
一些Max,INF的宏定义可由读者自行设定,这里并未给出,笔者能力有限,若有缺漏,还请指出。