C语言实现最短路径问题(Dijkstra算法)

本文受此文启发:最短路径问题—Dijkstra算法详解

实现内容:求出从某个顶点V0到其余顶点的最短路径的权值,以及具体路径
具体方法:
建立一个Dij结构,D存权值,Path存路径,visit保存此顶点是否被访问过

①根据邻接矩阵初始化D
②找出D中最小的顶点pos,并把visit[pos]设为1
③设置变量j从0增加到VexNum-1(遍历邻接矩阵第pos行),判断D[j]是否大于D[pos]+<pos,j>的权值(注意要设置判断条件 INT_MAX做加法会溢出成负数)
④如果大,那么就更新D[j]为D[pos]+<pos,j>的权值,同时更新D[j]的路径为D[pos]
⑤循环③④步,最多N-1次,注意,如果找不到最小的顶点需要跳出循环

测试图:
在这里插入图片描述
文本文档内容:
在这里插入图片描述
测试结果:
在这里插入图片描述
实现代码:

/*
	实现内容:Dijkstra算法
	VS2019 编译通过 王大花 2020.8.9
*/

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MaxVexNameLength 2
#define MaxPathLength 30
#define TextPath "C:\\Users\\86132\\Desktop\\第7章 作业\\最短路径\\ALGraph.txt"

char ch[MaxVexNameLength + 1];
//顶点结点
typedef struct _VexNode {
	char Date[MaxVexNameLength + 1];

}VexNode;

typedef struct _DIJ {
	char** Path;
	int* D;
	int* visit;

}DIJ;

//图
typedef struct _ALGraph {
	VexNode* Vertics;
	int VexNum, ArcNum;
	int** ArcMatrix;
	DIJ Dij;

}ALGraph;

//找到顶点位置
int Locate(char* ch) {
	return (3 == strlen(ch)) ? 10 * (ch[1] - '0') + ch[2] - '0' : ch[1] - '0';

}

void Creat_ALGraph(ALGraph* G) {

	FILE* fp = fopen(TextPath, "r");
	fscanf(fp, "%d%d", &G->VexNum, &G->ArcNum);

	//读取顶点信息 同时初始化邻接矩阵
	G->Vertics = (VexNode*)malloc(sizeof(VexNode) * G->VexNum);
	G->ArcMatrix = (int**)malloc(sizeof(int*) * G->VexNum);

	for (int i = 0; i < G->VexNum; i++) {
		fscanf(fp, "%s", G->Vertics[i].Date);
		//初始化邻接矩阵
		G->ArcMatrix[i] = (int*)malloc(sizeof(int) * G->VexNum);
		for (int j = 0; j < G->VexNum; j++) 
			G->ArcMatrix[i][j] = INT_MAX;
	}

	//录入弧的信息
	for (int i = 0; i < G->ArcNum; i++) {
		char ch1[MaxVexNameLength + 1], ch2[MaxVexNameLength + 1];
		int tmp;
		fscanf(fp, "%s%s%d", ch1, ch2, &tmp);
		G->ArcMatrix[Locate(ch1)][Locate(ch2)] = tmp;
	}

	//初始化Dij
	G->Dij.D = (int*)malloc(sizeof(int) * G->VexNum);
	G->Dij.Path = (char**)malloc(sizeof(char*) * G->VexNum);

	G->Dij.visit = (int*)malloc(sizeof(int) * G->VexNum);
	memset(G->Dij.visit, 0, sizeof(int) * G->VexNum);

	for (int i = 0; i < G->VexNum; i++)
		G->Dij.Path[i] = (char*)malloc(sizeof(char) * MaxPathLength);
		
	fclose(fp);
}

void Shortest_Path_DIJ(ALGraph G) {

	//获得初始顶点信息
	printf("请输入开始点数据:");
	scanf("%s", ch);

		//初始化
	for (int i = 0; i < G.VexNum; i++) {
		G.Dij.D[i] = G.ArcMatrix[Locate(ch)][i];
		strcpy(G.Dij.Path[i], ch);
	}

	G.Dij.D[Locate(ch)] = 0;
	G.Dij.visit[Locate(ch)] = 1;

	//最多剩N-1个顶点
	for (int i = 1; i < G.VexNum; i++) {

		//寻找距离最小顶点的位置
		int tmp = INT_MAX, pos = Locate(ch);
		for (int j = 0; j < G.VexNum; j++) {
			if (!G.Dij.visit[j] && G.Dij.D[j] < tmp) {
				tmp = G.Dij.D[j];
				pos = j;
			}
		}

		//如果V0没有出度了
		if (pos == Locate(ch))
			break;

		G.Dij.visit[pos] = 1;
		strcat(G.Dij.Path[pos], " -> ");
		strcat(G.Dij.Path[pos], G.Vertics[pos].Date);

		for (int j = 0; j < G.VexNum; j++) {
			if (INT_MAX != G.ArcMatrix[pos][j] && INT_MAX != G.Dij.D[pos]) {
				if (INT_MAX == G.Dij.D[pos] || G.Dij.D[j] > G.ArcMatrix[pos][j] + G.Dij.D[pos])
					G.Dij.D[j] = G.Dij.D[pos] + G.ArcMatrix[pos][j];
				
				G.Dij.Path[j][0] = '\0';
				strcat(G.Dij.Path[j], G.Dij.Path[pos]);
			}

		}
	}
}

void Print_Shortest_Path_DIJ(ALGraph G) {

	printf("\n最短路径:\n\n\n");
	for (int i = 0; i < G.VexNum; i++) {

		if (Locate(ch) == i)
			continue;
		if (G.Dij.visit[i]) {
			printf("路径:%-20s\t权重:%d\n", G.Dij.Path[i], G.Dij.D[i]);
		}
		else
			printf("路径:%s -> %s \t\t\t不存在\n", ch, G.Vertics[i].Date);
	}
}

int main() {

	ALGraph G;
	Creat_ALGraph(&G);

	Shortest_Path_DIJ(G);
	Print_Shortest_Path_DIJ(G);

	return 0;
}

Dijkstra算法是解决单源最短路径问题的一种经典算法,它可以用于有向图和无向图,但是不能处理负权边。下面是用C语言实现Dijkstra算法的基本步骤: 1. 初始化:将源点s到各个顶点的距离d[i]初始化为无穷大,将源点s到自身的距离d[s]初始化为0,将所有顶点标记为未访问。 2. 选取顶点:从未访问的顶点中选取距离源点s最近的顶点v作为当前顶点。 3. 更新距离:对于当前顶点v的每个邻接顶点w,如果通过v可以使s到w的距离变小,则更新d[w]的值。 4. 标记顶点:将顶点v标记为已访问。 5. 重复步骤2~4,直到所有顶点都被访问过或者找不到更短的路径。 下面是一个简单的C语言实现: ```c #include <stdio.h> #define INF 0x3f3f3f3f // 定义无穷大 int main() { int n, m, s, i, j; int u, v, w, min, pos; int d[1001], vis[1001], G[1001][1001]; // 输入n, m, s和邻接矩阵G scanf("%d%d%d", &n, &m, &s); for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) G[i][j] = INF; for (i = 1; i <= m; i++) { scanf("%d%d%d", &u, &v, &w); G[u][v] = w; G[v][u] = w; // 如果是无向图,这行可以去掉 } // 初始化 for (i = 1; i <= n; i++) { d[i] = G[s][i]; vis[i] = 0; } d[s] = 0; vis[s] = 1; // Dijkstra算法 for (i = 1; i < n; i++) { min = INF; for (j = 1; j <= n; j++) { if (!vis[j] && d[j] < min) { min = d[j]; pos = j; } } vis[pos] = 1; for (j = 1; j <= n; j++) { if (!vis[j] && d[pos] + G[pos][j] < d[j]) d[j] = d[pos] + G[pos][j]; } } // 输出结果 for (i = 1; i <= n; i++) printf("%d ", d[i]); printf("\n"); return 0; } ``` 输入格式: 第一行包含三个整数n, m, s,表示图的顶点数、边数和起点。 接下来m行,每行包含三个整数u, v, w,表示一条边从u到v,权值为w。 输出格式: 输出从起点s到每个顶点的最短距离。 时间复杂度:O(n^2) 空间复杂度:O(n^2)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值