Dijkstra和Floyd算法-旅游咨询系统C语言

目录

DIjkstra算法

Floyd算法

完整代码-应用:旅游咨询系统


DIjkstra算法

辅助数组

/*Dijkstra算法求单源最短路径的辅助数组*/

typedef struct {
	int S[MaxSpotNum + 1];//标记该顶点是否已求出最短路径
	ArcType Shortest[MaxSpotNum + 1];//记录当前到i的最短路径
	int PrePath[MaxSpotNum + 1];//记录当前最短路径的前驱顶点
}Dij_AssistArray;

思想:加点法,从v开始,每次在非S集合的点中选v到i距离最小的点,直到所有顶点包括进来。

求最短路径

//单源最短路径-求两景点最短路线
Dij_AssistArray D;
void ShortestPath_Dijkstra(AMGraph G, int v) {
	//初始化辅助数组
	for (int i = 1; i <= G.vexnum; i++) {
		D.S[i] = FALSE;//标记为未访问
		D.Shortest[i] = G.arcs[v][i];//初始化最短路径为起点v到i的路程

		if (D.Shortest[i] < INFINITY) {
			D.PrePath[i] = v;//有v到i的直达边,置前驱为v
		}
		else {
			D.PrePath[i] = -1;//v不能直达i,则i的前驱顶点序号置为-1
		}
	}

	D.S[v] = TRUE;
	D.Shortest[v] = 0;//自己到自己最短路径为0

	for (int i = 2; i <= G.vexnum; i++) {
		//在记录当前最短路径的辅助数组中找到权值最短的边对应的顶点w
		double min = INFINITY;//min记录最短路径长度
		int w;//w记录最短路径对应顶点的下标

		for (int j = 1; j <= G.vexnum; j++) {
			if (!D.S[j] && D.Shortest[j] < min) {
				min = D.Shortest[j];
				w = j;
			}
		}
		//找到了到w的最短路径,w加入S
		D.S[w] = TRUE;

		//更新辅助数组
		for (int j = 1; j <= G.vexnum; j++) {
			if (!D.S[j] && D.Shortest[w] + G.arcs[w][j] < D.Shortest[j]) {
				D.Shortest[j] = D.Shortest[w] + G.arcs[w][j];//更新当前最短路径
				D.PrePath[j] = w;//更改前驱
			}
		}
	}
}

打印最短路径:用到栈,从终点回溯存入栈中,再打印栈

//栈的操作
Status InitStack(PathStack* S) {
	S->top = 0;
	return OK;
}

Status StackIsEmpty(PathStack S) {
	if (S.top == 0) {
		return TRUE;
	}
	return FALSE;
}

Status Push(PathStack* S, int e) {
	if (S->top == MaxSpotNum) {
		return ERROR;
	}
	S->elem[S->top] = e;
	S->top++;
	return OK;
}

Status Pop(PathStack* S, int* e) {
	if (S->top == 0) {
		return ERROR;
	}
	S->top--;
	*e = S->elem[S->top];
}

void Print_ShortestPath_Dij(AMGraph G, int start) {
	printf("从%s出发,到其他所有景点的最短路径为:\n\n", G.vexs[start]);
	for (int end = 1; end <= G.vexnum; end++) {
		if (end == start) {//自己到自己的路径不必打印
			continue;
		}
		PathStack S;
		InitStack(&S);
		Push(&S, end);
		//从终点end找最短路径的前驱节点,若不是起点序号start则进栈
		int tem1, tem2;
		for (tem1 = D.PrePath[end]; tem1 != start; tem1 = D.PrePath[tem2]) {
			Push(&S, tem1);
			tem2 = tem1;
		}
		printf("*%s", G.vexs[start]);
		tem1 = start;//tem1记录前驱顶点
		double sum = 0;
		while (!StackIsEmpty(S)) {
			Pop(&S, &tem2);
			printf("-%.1lfkm->%s", G.arcs[tem1][tem2], G.vexs[tem2]);
			sum += G.arcs[tem1][tem2];
			tem1 = tem2;
		}
		printf("\n*总路程为%.1lfkm.\n\n", sum);
	}
}

Floyd算法

思想:加边法,把边的权值从小到大排列,从小边开始选起,直到选了n-1条边(n是顶点数)

辅助数组

/*Floyd算法求任意两顶点最短路径的辅助数组*/

typedef struct {
	ArcType Shortest[MaxSpotNum + 1][MaxSpotNum + 1];//记录当前最短路径
	int PrePath[MaxSpotNum + 1][MaxSpotNum + 1];//记录当前最短路径的前驱顶点
}Floyd_AssistArray;

求任意两点间最短路径

//求任意两景点间最短路径
Floyd_AssistArray F;//辅助数组

void ShortestPath_Floyd(AMGraph G) {
	//初始化辅助数组
	for (int i = 1; i <= G.vexnum; i++) {
		for (int j = 1; j <= G.vexnum; j++) {
			if (i == j) {
				F.Shortest[i][j] = 0;//自己到自己为0
			}
			else {
				F.Shortest[i][j] = G.arcs[i][j];
			}
			if (F.Shortest[i][j] < INFINITY && i != j) {//有直达边
				F.PrePath[i][j] = i;
			}
			else {
				F.PrePath[i][j] = -1;//i与j无直达边,前驱置为-1
			}
		}
	}
	//k表示每次加的顶点
	for (int k = 1; k <= G.vexnum; k++) {
		for (int i = 1; i <= G.vexnum; i++) {
			for (int j = 1; j <= G.vexnum; j++) {
				if (F.Shortest[i][j] > F.Shortest[i][k] + F.Shortest[k][j]) {
					F.Shortest[i][j] = F.Shortest[i][k] + F.Shortest[k][j];
					F.PrePath[i][j] = F.PrePath[k][j];
				}
			}
		}
	}
}

完整代码-应用:旅游咨询系统

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

#define FALSE 0
#define TRUE 1
#define ERROR 0
#define OK 1
typedef int Status;

#define MaxSpotNum 20//系统中存有的最大景点个数
#define SpotName 30//景点名字字符串长度
#define INFINITY 32767
typedef double ArcType;//定义两景点间路程km的数据类型为double

/*邻接矩阵的存储结构*/
typedef struct {
	char vexs[MaxSpotNum + 1][SpotName];//景点表,0号元素不存
	ArcType arcs[MaxSpotNum + 1][MaxSpotNum + 1];//邻接矩阵
	int vexnum, arcnum;//当前顶点数、边数
}AMGraph;

/*Dijkstra算法求单源最短路径的辅助数组*/
typedef struct {
	int S[MaxSpotNum + 1];//标记该顶点是否已求出最短路径
	ArcType Shortest[MaxSpotNum + 1];//记录当前到i的最短路径
	int PrePath[MaxSpotNum + 1];//记录当前最短路径的前驱顶点
}Dij_AssistArray;

/*Floyd算法求任意两顶点最短路径的辅助数组*/
typedef struct {
	ArcType Shortest[MaxSpotNum + 1][MaxSpotNum + 1];//记录当前最短路径
	int PrePath[MaxSpotNum + 1][MaxSpotNum + 1];//记录当前最短路径的前驱顶点
}Floyd_AssistArray;

/*顺序栈的相关定义与声明*/
typedef struct {
	int elem[MaxSpotNum];//记录最短路径上的顶点序号
	int top;
}PathStack;

Status InitStack(PathStack* S);
Status StackIsEmpty(PathStack S);
Status Push(PathStack* S, int e);
Status Pop(PathStack* S, int* e);

//从文本读取景点信息并打印景点及对应标号
void PrintMessage(AMGraph G);
//从文本中读取景点名字和距离数据,创建无向网的邻接矩阵
void CreateMap(AMGraph* G);
//打印景点图的邻接矩阵
void PrintMap(AMGraph G);
//功能菜单
void Menu(void);

//求景点对应的序号
int LocateSpot(AMGraph G, char* name);
//单源最短路径-求两景点最短路线
void ShortestPath_Dijkstra(AMGraph G, int v);
//打印Dijkstra算法求出的最短路径
void Print_ShortestPath_Dij(AMGraph G, int start);
//求任意两景点间最短路径
void ShortestPath_Floyd(AMGraph G);
//打印Floyd算法求出的最短路径
void Print_ShortestPath_Floyd(AMGraph G, int start, int end);

int main() {
	AMGraph G;
	CreateMap(&G);
	printf("************************** 与您同行,与您同心 **************************\n");
	printf("\n* 欢迎使用“与您同行”旅游咨询系统\n");
	printf("\n* 我们提供路线咨询服务的景点有:\n\n");
	PrintMessage(G);

	Menu();
	char choice;
	scanf("%c", &choice);
	getchar();
	while (choice != 'd') {
		switch (choice) {
		case 'a'://打印景点图的邻接矩阵
			PrintMap(G);
			break;
		case 'b'://单源最短路径-某一景点到其他景点的最短路径
			PrintMessage(G);
			printf("\n请输入你想查询的景点对应的序号:\n");
			int v;
			scanf("%d", &v);
			getchar();
			if (v<1 || v>G.vexnum) {
				printf("您输入的景点序号有误!\n");
			}
			else {
				ShortestPath_Dijkstra(G, v);
				Print_ShortestPath_Dij(G, v);
			}
			break;
		case 'c'://两景点间的最短路径
			PrintMessage(G);
			printf("\n请分别输入起点和终点的对应序号:\n");
			int v1, v2;
			scanf("%d%d", &v1, &v2);
			getchar();
			if (v1<1 || v1>G.vexnum || v2<1 || v2>G.vexnum) {
				printf("您输入的景点序号有误!\n");
			}
			else {
				ShortestPath_Floyd(G);
				Print_ShortestPath_Floyd(G, v1, v2);
			}
			break;
		default:
			printf("您的输入有误,请重新输入!\n");
			break;
		}
		Menu();
		scanf("%c", &choice);
		getchar();
	}
	printf("\n查询服务到此结束,望您满意!\n");

	return 0;
}

//菜单
void Menu(void) {
	printf("\n* 我们提供以下查询服务:\n");
	printf("\n*************************************************************************\n");
	printf("*     a-“我不知道去哪,想看看有哪些景点”                              *\n");
	printf("*     b-“我想查询某个景点到其他景点的最短路线                          *\n");
	printf("*     c-“我想查询两个景点间的最短路线,并想知道沿途有哪些景点”        *\n");
	printf("*     d- 退出系统.                                                      *\n");
	printf("*************************************************************************\n");
	printf("\n请输入您的选择:\n");
}

//打印系统中存有的景点及其对应序号
void PrintMessage(AMGraph G) {
	for (int i = 1; i <= G.vexnum; i++) {
		printf("*%-3d- %-18s", i, G.vexs[i]);
		if (i % 3 == 0) {
			printf("\n");
		}
	}
}

//求景点对应的序号
int LocateSpot(AMGraph G, char* name) {
	for (int i = 1; i <= G.vexnum; i++) {
		if (strcmp(G.vexs[i], name) == 0) {
			return i;
		}
	}
}

//创建邻接矩阵,从文本读入信息
void CreateMap(AMGraph* G) {
	FILE* fp = fopen("spot.txt", "r");
	if (fp == NULL) {
		puts("文件打开失败.\n");
		exit(0);
	}

	//从spot.txt读取景点个数和景点名字
	fscanf(fp, "%d", &G->vexnum);
	for (int i = 1; i <= G->vexnum; i++) {
		fscanf(fp, "%s", G->vexs[i]);
	}
	fclose(fp);

	//初始化矩阵:权值置为无穷大
	for (int i = 0; i <= G->vexnum; i++) {
		for (int j = 0; j <= G->vexnum; j++) {
			G->arcs[i][j] = INFINITY;
		}
	}

	//从distance.txt读取路径及距离信息
	fp = fopen("distance.txt", "r");
	int n = 0;//记录有多少条边
	char name1[30], name2[30];
	double t_km;//暂存边的权值
	while (fscanf(fp, "%s%s%lf", name1, name2, &t_km) != EOF) {
		n++;
		int i = LocateSpot(*G, name1);
		int j = LocateSpot(*G, name2);
		G->arcs[i][j] = t_km;
		G->arcs[j][i] = t_km;
	}
	G->arcnum = n;//当前边数
}

//打印景点图的邻接矩阵
void PrintMap(AMGraph G) {
	printf("\n景点及其对应序号为:\n");
	PrintMessage(G);
	printf("\n景点地图为:\n");
	printf("序号  ");
	for (int i = 1; i <= G.vexnum; i++) {
		printf("*%-4d", i);
	}
	printf("\n");
	for (int i = 1; i <= G.vexnum; i++) {
		printf("*%-5d", i);
		for (int j = 1; j <= G.vexnum; j++) {
			if (G.arcs[i][j] == INFINITY) {
				//用空格代替没有直达路径的两景点
				printf("%-5c", 32);
				continue;
			}
			printf("%-5.1lf", G.arcs[i][j]);
		}
		printf("\n");
	}
	printf("空格表示两景点没有直达路径.\n");
}

//栈的操作
Status InitStack(PathStack* S) {
	S->top = 0;
	return OK;
}

Status StackIsEmpty(PathStack S) {
	if (S.top == 0) {
		return TRUE;
	}
	return FALSE;
}

Status Push(PathStack* S, int e) {
	if (S->top == MaxSpotNum) {
		return ERROR;
	}
	S->elem[S->top] = e;
	S->top++;
	return OK;
}

Status Pop(PathStack* S, int* e) {
	if (S->top == 0) {
		return ERROR;
	}
	S->top--;
	*e = S->elem[S->top];
}

//单源最短路径-求两景点最短路线
Dij_AssistArray D;
void ShortestPath_Dijkstra(AMGraph G, int v) {
	//初始化辅助数组
	for (int i = 1; i <= G.vexnum; i++) {
		D.S[i] = FALSE;
		D.Shortest[i] = G.arcs[v][i];//初始化最短路径为起点v到i的路程
		if (D.Shortest[i] < INFINITY) {
			D.PrePath[i] = v;
		}
		else {
			D.PrePath[i] = -1;//v不能直达i,则i的前驱顶点序号置为-1
		}
	}
	D.S[v] = TRUE;
	D.Shortest[v] = 0;
	for (int i = 2; i <= G.vexnum; i++) {
		//在记录当前最短路径的辅助数组中找到权值最短的边对应的顶点w
		double min = INFINITY;//min记录最短路径长度
		int w;//w记录最短路径对应顶点的下标
		for (int j = 1; j <= G.vexnum; j++) {
			if (!D.S[j] && D.Shortest[j] < min) {
				min = D.Shortest[j];
				w = j;
			}
		}
		//找到了到w的最短路径,w加入S
		D.S[w] = TRUE;
		//更新辅助数组
		for (int j = 1; j <= G.vexnum; j++) {
			if (!D.S[j] && D.Shortest[w] + G.arcs[w][j] < D.Shortest[j]) {
				D.Shortest[j] = D.Shortest[w] + G.arcs[w][j];//更新当前最短路径
				D.PrePath[j] = w;//更改前驱
			}
		}
	}
}

void Print_ShortestPath_Dij(AMGraph G, int start) {
	printf("从%s出发,到其他所有景点的最短路径为:\n\n", G.vexs[start]);
	for (int end = 1; end <= G.vexnum; end++) {
		if (end == start) {//自己到自己的路径不必打印
			continue;
		}
		PathStack S;
		InitStack(&S);
		Push(&S, end);
		//从终点end找最短路径的前驱节点,若不是起点序号start则进栈
		int tem1, tem2;
		for (tem1 = D.PrePath[end]; tem1 != start; tem1 = D.PrePath[tem2]) {
			Push(&S, tem1);
			tem2 = tem1;
		}
		printf("*%s", G.vexs[start]);
		tem1 = start;//tem1记录前驱顶点
		double sum = 0;
		while (!StackIsEmpty(S)) {
			Pop(&S, &tem2);
			printf("-%.1lfkm->%s", G.arcs[tem1][tem2], G.vexs[tem2]);
			sum += G.arcs[tem1][tem2];
			tem1 = tem2;
		}
		printf("\n*总路程为%.1lfkm.\n\n", sum);
	}
}

//求任意两景点间最短路径
Floyd_AssistArray F;
void ShortestPath_Floyd(AMGraph G) {
	//初始化辅助数组
	for (int i = 1; i <= G.vexnum; i++) {
		for (int j = 1; j <= G.vexnum; j++) {
			if (i == j) {
				F.Shortest[i][j] = 0;//自己到自己为0
			}
			else {
				F.Shortest[i][j] = G.arcs[i][j];
			}
			if (F.Shortest[i][j] < INFINITY && i != j) {//有直达边
				F.PrePath[i][j] = i;
			}
			else {
				F.PrePath[i][j] = -1;//i与j无直达边,前驱置为-1
			}
		}
	}
	//k表示每次加的顶点
	for (int k = 1; k <= G.vexnum; k++) {
		for (int i = 1; i <= G.vexnum; i++) {
			for (int j = 1; j <= G.vexnum; j++) {
				if (F.Shortest[i][j] > F.Shortest[i][k] + F.Shortest[k][j]) {
					F.Shortest[i][j] = F.Shortest[i][k] + F.Shortest[k][j];
					F.PrePath[i][j] = F.PrePath[k][j];
				}
			}
		}
	}
}

void Print_ShortestPath_Floyd(AMGraph G, int start, int end) {
	printf("从%s出发,到%s的最短路径为:\n\n", G.vexs[start], G.vexs[end]);
	PathStack S;
	InitStack(&S);
	Push(&S, end);
	//从终点end找最短路径的前驱节点存入tem1,若不是起点序号start则进栈
	int tem1, tem2;
	for (tem1 = F.PrePath[start][end]; tem1 != start; tem1 = F.PrePath[start][tem2]) {
		Push(&S, tem1);
		tem2 = tem1;
	}
	printf("*%s", G.vexs[start]);
	tem1 = start;//tem1记录前驱顶点
	double sum = 0;
	while (!StackIsEmpty(S)) {
		Pop(&S, &tem2);
		printf("-%.1lfkm->%s", G.arcs[tem1][tem2], G.vexs[tem2]);
		sum += G.arcs[tem1][tem2];
		tem1 = tem2;
	}
	printf("\n*总路程为%.1lfkm.\n\n", sum);
}

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值