【数据结构】图的最短路径问题(Dijkstra算法、Floyd算法)(C语言)

1.求某一顶点到其它各顶点的最短路径(Dijkstra算法)

设有带权的有向图D=(V,{E}),已知源点为v0,求v0到其它各顶点的最短路径。

1.1 迪杰斯特拉(Dijkstra)算法

Dijkstra算法按照最短路径长度递增的顺序产生一点到其余各顶点的所有最短路径。
对于图G=(V,{E}),将图中的顶点分成以下两组:
第一组S:已求出最短路径的终点的集合(开始为{v0})。
第二组V-S:尚未求出最短路径的顶点的集合(开始为除v0的全部结点)。
算法将按最短路径长度的递增顺序逐个将第二组的顶点加入到第一组中,直到所有顶点都被加入到第一组的顶点集S为止。

算法中使用了辅助数组dist[],dist[i]表示目前已经找到的v0->vi的最短路径长度,若v0到vi有弧,则它的初值为权值,否则为∞。
每加入一个新的顶点vk到顶点集S中,对于第二组剩余的各个顶点而言,就多了一个可以中转的结点vk,从而需要对第二组中剩余的各个顶点的当前最短路径长度dist[i]进行修正。修正后再选择数组dist[]中最小值的顶点加入到第一组顶点集S中,如此进行下去,直到所有顶点都在顶点集S中为止。
为了记录从v0到其余各顶点的最短路径,引进辅助数组path[],path[i]表示目前已经找到的v0->vi的最短路径序列。
带权有向图

算法步骤示例
初始化
S的初值:{v0}

i=1 i=2 i=3 i=4 i=5
dist[ ]初值 50 10 45
path[ ]初值 v0,v1 v0,v2 v0,v4

从v0到各顶点的最短路径的求解过程

1 2 3 4 5
v1 50(v0,v1) 50(v0,v1) 45(v0,v2,v3,v1)
v2 10(v0,v2)
v3 ∞( ) 25(v0,v2,v3)
v4 45(v0,v4) 45(v0,v4) 45(v0,v4) 45(v0,v4)
v5 ∞( ) ∞( ) ∞( ) ∞( ) ∞( )
S (v0,v2) (v0,v2,v3) (v0,v2,v3,v1) (v0,v2,v3,v1,v4) (v0,v2,v3,v1,v4)

1.2 完整代码+注释

/*图的最短路径——Dijkstra算法*/
# include<stdio.h>
# define MAX_VERTEX_NUM 20			//最多顶点个数
# define INFINITY 32768

/*图的邻接矩阵表示法*/
typedef int AdjType;
typedef char VertexData;
typedef struct ArcNode {
    
    
	AdjType adj;							//无权图用1或0表示是否相邻,带权图则为权值类型
}ArcNode;
typedef struct {
    
    
	VertexData vertex[MAX_VERTEX_NUM];				//顶点向量
	ArcNode arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];	//邻接矩阵
	int vexnum, arcnum;								//图的顶点数和弧数
}AdjMatrix;

/*采用邻接矩阵表示法创建有向网*/
/*求顶点位置*/
int LocateVertex(AdjMatrix* G, VertexData v) {
    
    
	int k;
	for (k = 0; k < G->vexnum; k++) {
    
    
		if (G->vertex[k] == v)
			break;
	}
	return k;
}

/*创建有向网*/
int CreateDN(AdjMatrix* G) {
    
    
	int i, j, k, weight;
	VertexData v1, v2;
	printf("输入图的顶点数和弧数:");		//输入图的顶点数和弧数
	scanf("%d%d", &G->vexnum, &G->arcnum);
	for (i = 0; i < G->vexnum; i++) {
    
    		//初始化邻接矩阵
		for (j = 0; j < G->vexnum; j++)
			G->arcs[i][j].adj = INFINITY;
	}
	printf("输入图的顶点:");
	for (i = 0; i < G->vexnum; i++)			//输入图的顶点
		scanf(" %c", &G->vertex[i]);
	for (k = 0; k < G->arcnum; k++) {
    
    
		printf("输入第%d条弧的两个顶点及权值:", k + 1);
		scanf(" %c %c %d", &v1, &v2, &weight);	//输入一条弧的两个顶点及权值
		i = LocateVertex(G, v1);
		j = LocateVertex(G, v2);
		G->arcs[i][j].adj = weight;			//建立弧
	}
}

/*顶点集合,用于存放顶点和路径*/
typedef struct {
    
    
	char elem[MAX_VERTEX_NUM];
	int last;
}VertexSet, PathSet[MAX_VERTEX_NUM];

PathSet path;								//path[i]中存放顶点i的当前最短路径
VertexSet S;								//S为已找到最短路径的终点的集合

/*路径集合初始化*/
void InitPathSet(PathSet path, int i) {
    
    
	path[i].last = -1;
}
/*顶点集合初始化*/
void InitVertexSet(VertexSet* S) {
    
    
	S->last = -1;
}

/*路径集合添加操作*/
void AddPath(PathSet path, int i, char c) {
    
    
	path[i].last++;
	path[i].elem[path[i].last] = c;
}

/*顶点集合添加操作*/
void AddVertex(VertexSet* S, char c) {
    
    
	S->last++;
	S->elem[S->last] = c;
}

/*判断顶点vi是否在顶点集合S中*/
int IsMember(char c) {
    
    
	int i;
	for (i = 0; i <= S.last; i++) {
    
    
		if (S.elem[i] == c) {
    
    
			return 1;
		}
	}
	return 0;
}

/*Dijkstra算法*/
void ShortestPath_DJS(AdjMatrix G, int v0, int dist[]) {
    
    
//dist[i]中存放顶点i的当前最短路径长度
	int i, k, t;
	for (i = 0; i < G.vexnum; i++) {
    
    		//初始化dist[i]和path[i]
		InitPathSet(path, i);
		dist[i] = G.arcs[v0][i].adj;
		if (dist[i] < INFINITY) {
    
    			//若v0和vi之间有路径,则将该路径添加到对应的path[i]中
			AddPath(path, i, G.vertex[v0]);
			AddPath(path, i, G.vertex[i]);
		}
	}
	InitVertexSet(&S);						//初始化S
	AddVertex(&S, G.vertex[v0]);			//将v0看成第一个已找到最短路径的终点

	for (t = 1; t < G.vexnum; t++) {
    
    		//求v0到其它顶点的最短路径
		int min = INFINITY;
		for (i = 0; i < G.vexnum; i++) {
    
    
			if (!IsMember(G.vertex[i]) && dist[i] < min) {
    
    //求Min(dist[i])
				k = i;
				min = dist[i];
			}
		}
		if (min == INFINITY)				//说明v0到其它顶点已无路径
			return;
		AddVertex(&S, G.vertex[k]);			//将vk加入S集合,说明已找到v0到vk的最短路径
		for (i = 0; i < G.vexnum; i++) {
    
    	//修正dist[i],i∈V-S
			//当前顶点vi不在集合S中 且 vk到vi有路径 且 经vk到vi的路径比原最短路径要短
			if (!IsMember(G.vertex[i]) && G.arcs[k][i].adj != INFINITY && (dist[k] + G.arcs[k][i].adj < dist[i])) {
    
    
				dist[i]=dist[k]+ G.arcs[k][i].adj;
				path[i] = path[k];
				AddPath(path, i, G.vertex[i]);//path[i] = path[k]∪{vi}
			}
		}
	}
}

int main() {
    
    
	AdjMatrix G;
	int dist[MAX_VERTEX_NUM], i, j;
	CreateDN(&G);
	ShortestPath_DJS(G, 0, dist);
	printf("\n");
	for (i = 1; i < G.vexnum; i++) {
    
    		//输出最短路径
		printf("v0->v%c的最短路径:", G.vertex[i]);
		if (dist[i] != INFINITY) {
    
    
			for (j = 0; j <= path[i].last; j++)
				printf("v%c ", path[i].elem[j]);
			printf("\t路径长度为%d", dist[i]);
			printf("\n");
		}
		else
			printf("无法到达!\n");
	}
	return 0;
}

1.3 运行结果

运行结果1

2.求任意一对顶点间的最短路径(Floyd算法)

2.1 弗罗伊德(Floyd)算法

求图中任意顶点vi到vj的最短路径。
① 将vi到vj的最短路径长度初始化为对应邻接矩阵G.arcs[i][j]值,然后进行如下n次比较和修正:
② 在vi和vj间加入顶点v0,比较(vi,v0,vj)和(vi,vj)的路径长度,取较短的路径作为vi到vj的当前最短路径,如此类推依次加入v1、v2…经过n次比较和修正,得到vi到vj的最短路径。
带权有向图

1.2 完整代码+注释

/*图的最短路径——Floyd算法*/
# include<stdio.h>
# define MAX_VERTEX_NUM 20			//最多顶点个数
# define INFINITY 32768

/*图的邻接矩阵表示法*/
typedef int AdjType;
typedef char VertexData;
typedef struct ArcNode {
    
    
	AdjType adj;							//无权图用1或0表示是否相邻,带权图则为权值类型
}ArcNode;
typedef struct {
    
    
	VertexData vertex[MAX_VERTEX_NUM];				//顶点向量
	ArcNode arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];	//邻接矩阵
	int vexnum, arcnum;								//图的顶点数和弧数
}AdjMatrix;

/*采用邻接矩阵表示法创建有向网*/
/*求顶点位置*/
int LocateVertex(AdjMatrix* G, VertexData v) {
    
    
	int k;
	for (k = 0; k < G->vexnum; k++) {
    
    
		if (G->vertex[k] == v)
			break;
	}
	return k;
}

/*创建有向网*/
int CreateDN(AdjMatrix* G) {
    
    
	int i, j, k, weight;
	VertexData v1, v2;
	printf("输入图的顶点数和弧数:");		//输入图的顶点数和弧数
	scanf("%d%d", &G->vexnum, &G->arcnum);
	for (i = 0; i < G->vexnum; i++) {
    
    		//初始化邻接矩阵
		for (j = 0; j < G->vexnum; j++)
			G->arcs[i][j].adj = INFINITY;
	}
	printf("输入图的顶点:");
	for (i = 0; i < G->vexnum; i++)			//输入图的顶点
		scanf(" %c", &G->vertex[i]);
	for (k = 0; k < G->arcnum; k++) {
    
    
		printf("输入第%d条弧的两个顶点及权值:", k + 1);
		scanf(" %c %c %d", &v1, &v2, &weight);	//输入一条弧的两个顶点及权值
		i = LocateVertex(G, v1);
		j = LocateVertex(G, v2);
		G->arcs[i][j].adj = weight;			//建立弧
	}
}

/*顶点集合,用于存放顶点和路径*/
typedef struct {
    
    
	char elem[MAX_VERTEX_NUM];
	int last;
}PathSet[MAX_VERTEX_NUM][MAX_VERTEX_NUM];

PathSet path;								//path[i][j]中存放vi到vj的当前最短路径

/*路径集合初始化*/
void InitPathSet(PathSet path, int i, int j) {
    
    
	path[i][j].last = -1;
}

/*路径集合添加操作*/
void AddPath(PathSet path, int i, int j, char c) {
    
    
	path[i][j].last++;
	path[i][j].elem[path[i][j].last] = c;
}

/*两个路径集合path[i][k]和path[k][j]合并操作*/
void Join(PathSet path, int i, int j, int k) {
    
    
	int t;
	for (t = 0; t <= path[i][k].last; t++)
		path[i][j].elem[t] = path[i][k].elem[t];
	for (t = path[i][k].last + 1; t <= path[i][k].last + path[k][j].last; t++)
		path[i][j].elem[t] = path[k][j].elem[t - path[i][k].last];
	path[i][j].last = path[i][k].last + path[k][j].last;
}

int dist[MAX_VERTEX_NUM][MAX_VERTEX_NUM];	//dist[i][j]为vi到vj的当前最短路径长度

/*Floyd算法*/
void ShortestPath_DJS(AdjMatrix G) {
    
    
	int i, j, k;
	for (i = 0; i < G.vexnum; i++) {
    
    		//初始化dist[i][j]和path[i][j]
		for (j = 0; j < G.vexnum; j++) {
    
    
			InitPathSet(path, i, j);
			dist[i][j] = G.arcs[i][j].adj;
			if (dist[i][j] < INFINITY) {
    
    
				AddPath(path, i, j, G.vertex[i]);//若vi和vj之间有路径,则将该路径添加到对应的path[i][j]中
				AddPath(path, i, j, G.vertex[j]);
			}
		}
	}
	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];
					Join(path, i, j, k);	//两个路径集合path[i][k]和path[k][j]合并
				}
			}
		}
	}
}

int main() {
    
    
	int i, j, k;
	AdjMatrix G;
	CreateDN(&G);
	ShortestPath_DJS(G);
	printf("\n");
	for (i = 0; i < G.vexnum; i++) {
    
    
		for (j = 0; j < G.vexnum; j++) {
    
    
			if (i != j) {
    
    
				printf("v%c->v%c的最短路径:", G.vertex[i], G.vertex[j]);
				if (dist[i][j] < INFINITY) {
    
    
					for (k = 0; k <= path[i][j].last; k++)
						printf("v%c ", path[i][j].elem[k]);
					printf("\t路径长度为:%d", dist[i][j]);
					printf("\n");
				}
				else
					printf("无法到达!\n");
			}
		}
	}
	return 0;
}

2.3 运行结果

运行结果2
参考:耿国华《数据结构——用C语言描述(第二版)》

更多数据结构内容关注我的《数据结构》专栏https://blog.csdn.net/weixin_51450101/category_11514538.html?spm=1001.2014.3001.5482

猜你喜欢

转载自blog.csdn.net/weixin_51450101/article/details/123074269
今日推荐