目录
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);
}