Dijkstra蒜法—C语言实现(邻接表)

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的宏定义可由读者自行设定,这里并未给出,笔者能力有限,若有缺漏,还请指出。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值