数据结构课程设计是在学完数据结构课程之后的实践教学环节。本环节是培养学生的数据抽象能力,是进行复杂程序设计的学习训练过程;要求学生能对所涉及问题进行分析,选择合适的数据结构、存储结构及相应的算法,设计解题方案,并编写正确、完整的程序,从而进一步提高程序设计技能和技巧。
一.散列表
一.设计目的
1.哈希表是一种重要的数据结构,学习哈希表会加深对数据结构和算法的理解。
2.学习如何应用哈希表提高了如果更好地解决实际的工程和编程问题的能力
3.学习哈希表会锻炼代码设计和模块化思维,更擅长构建复杂系统。
二.设计方案
1.问题描述
针对同班同学信息设计一个通讯录,学生信息有姓名,学号,电话号码等,以学生姓名为关键字设计哈希表,并完成相应的建表和查表程序。
基本要求:
1)姓名以汉语拼音形式,待填入哈希表的人名约 40个,
2)用链地址法处理冲突;
3)完成按姓名查询的操作,并在查找的过程中给出比较的次数。
4)实现信息的增、删、改。将初始班级的通讯录信息存入文件
2.数据结构
1)学生信息结构体:
typedef struct {
char name[MAX_NAME_LEN];
int student_number;
char phone_number[12];
char address[MAX_ADDRESS_LEN];
} Student;
用于表示学生的基本信息,包括姓名,电话号码,学号和家庭住址。
2)哈希表节点结构体
typedef struct HashNode {
Student student;
struct HashNode *next;
} HashNode;
表示哈希表的节点,包括一个student结构体作为数据以及指向下一个节点的指针‘next’。
3)哈希表结构体
typedef struct {
HashNode *table[TABLE_SIZE];
} HashTable;
表示整个哈希表,包括一个固定长度数组‘table’,每个数组元素是一个指向hashnode结构体的指针,用于处理哈希冲突的链表。
4)哈希函数
int hash(char *name) {
int sum = 0;//累加字符的as值
int i = 0;//索引
while (name[i] != '\0') {
sum += name[i];
i++;
}
return sum % TABLE_SIZE;
}
该函数接受一个学生姓名作为输入,通过对姓名中字符ascll码进行求和,然后取余数,得到一个哈希表的索引。
3.逻辑设计
1)数据结构定义:
定义了学生信息结构体 Student,包含姓名、学号、电话号码和家庭住址等信息。
定义了哈希表节点结构体 HashNode,包含一个学生信息和指向下一个节点的指针。
定义了哈希表结构体 HashTable,包含一个固定大小的数组,每个元素是指向哈希表节点的指针。
2)哈希函数:
使用字符的 ASCII 码值的累加和来计算学生姓名的哈希值。这个哈希值被用于确定学生信息在哈希表中的位置。
int hash(char *name) {…}
3)查询学生信息函数:
根据输入的学生姓名,通过哈希函数计算索引,然后在哈希表中查找相应的节点。如果找到,输出学生信息;如果未找到,返回未找到的消息。
int search(HashTable *hash_table, char *name, int *compares) {…}
4)插入学生信息函数:
根据学生姓名计算哈希值,然后将学生信息插入哈希表的相应位置。如果该位置已经有其他节点,将新节点追加到链表的末尾。
void insert(HashTable *hash_table, Student student) {…}
5)删除学生信息函数:
根据学生姓名计算哈希值,然后在哈希表中查找该节点。如果找到,删除节点并释放内存;如果未找到,返回删除失败的消息。
int delete (HashTable *hash_table, char *name) {…}
6)修改学生信息函数
根据学生姓名计算哈希值,然后在哈希表中查找该节点。如果找到,修改节点的信息为新的学生信息;如果未找到,返回修改失败的消息。
int modify(HashTable *hash_table, char *name, Student new_student) {…}
7)文件读写函数:
从文件中读取初始班级的通讯录信息,将学生信息加载到哈希表中。
将当前哈希表中的学生信息保存到文件。
void loadInitialData(HashTable *hash_table) {…}
void saveDataToFile(HashTable *hash_table) {…}
8)主函数:
创建哈希表,初始化为空。通过加载文件初始化哈希表。
int main() {…}
提供用户菜单进行查询、插入、删除、修改等操作,持续循环直到用户选择退出。
三.设计流程图
四.实现代码(部分)
哈希表结构定义:
这里定义了学生信息的结构体 Student,哈希表节点的结构体 HashNode,以及哈希表的结构体 HashTable。
// 学生信息结构体
typedef struct {
char name[MAX_NAME_LEN];
int student_number;
char phone_number[12];
char address[MAX_ADDRESS_LEN];
} Student;
// 哈希表节点结构体
typedef struct HashNode {
Student student;
struct HashNode *next;
} HashNode;
// 哈希表结构体
typedef struct {
HashNode *table[TABLE_SIZE];
} HashTable;
哈希函数
这是一个简单的哈希函数,将学生姓名中每个字符的 ASCII 值相加,然后取余数得到哈希表的索引。
int hash(char *name) {
int sum = 0;//累加字符的as值
int i = 0;//索引
while (name[i] != '\0') {
sum += name[i];
i++;
}
return sum % TABLE_SIZE;
}
向哈希表中插入学生信息
该函数将一个学生信息插入到哈希表中,如果哈希表的对应索引位置为空,直接插入;如果不为空,则遍历链表,找到尾部插入。
void insert(HashTable *hash_table, Student student) {
int index = hash(student.name);
HashNode *new_node = (HashNode *)malloc(sizeof(HashNode));
new_node->student = student;
new_node->next = NULL;
if (hash_table->table[index] == NULL) {
hash_table->table[index] = new_node;
} else {
HashNode *current = hash_table->table[index];
while (current->next != NULL) {
current = current->next;
}
current->next = new_node;
}
}
在哈希表中查找学生信息
该函数用于在哈希表中查找学生信息,返回是否找到,并输出学生信息。
int search(HashTable *hash_table, char *name, int *compares) {
int index = hash(name);
HashNode *current = hash_table->table[index];
*compares = 0;
while (current != NULL) {
(*compares)++;
if (strcmp(name, current->student.name) == 0) {
printf("学生姓名:%s\n学号:%d\n电话号码:%s\n家庭住址:%s\n", current->student.name,
current->student.student_number, current->student.phone_number, current->student.address);
return 1; // 学生信息找到
}
current = current->next;
}
return 0; // 学生信息未找到
}
五.实现结果
二.校园导游程序
1.提高使用结构体、数组和矩阵等数据结构的掌握情况。
2.学习如何将代码结构化为函数和模块有助于提升代码的可用性和可读性。
3.提尕个人算法思维。
4.提增强了错误处理的能力。
二.设计方案
1.问题描述
设计一个校园导游程序为来访的客人提供各种信息查询。(校园平面是一个无向图)
参考图示:
基本要求:
1)设计学校的旗山校区北区校园平面图,所含场所不少于 10 个。以图中顶点表示校
内各场所,存放场所名称、代号、简介等信息;以边表示路径,存放路径长度等相关信息。
2)为来访客人提供图中任意场所相关信息的查询。
3)为来访客人提供图中任意场所的问路查询,即查询任意两个景点之间的一条最短的简单路径。
要求:(1)实现场所增加、删除、查询
(2)实现路径的增加、删除
(3)数据的保存、调入。
2.数据结构
1)side 结构体:
用于表示边的权值。
在程序中被用作邻接矩阵的元素类型。
2)vetinf 结构体:
用于表示顶点的信息,包括编号、名称和简介。
在程序中被用作存储顶点数组的元素类型。
3)mapstr 结构体:
用于表示整个图的结构信息,包括顶点数组、邻接矩阵以及图的顶点数和边数。
包含了 vetinf 和 side 类型的数组。此外,程序使用了全局变量 campus 来表示学校校园的图结构,以及全局数组 shortest 和 5)pathh 用于存储最短路径信息。
主要函数和其功能包括:
6)initmap 函数:
用于初始化图的结构,包括顶点数组和邻接矩阵。
7)visit 函数:
用于访问结点信息,即读取并显示指定顶点的详细信息。
8)floyd 函数:
实现了弗洛伊德算法,计算图中各个顶点之间的最短路径。
9)display 函数:
用于输出最短路径信息。
10)shortdistance 函数:
允许用户查询任意两点间的最短路径和距离。
11)menu 函数:
显示用户操作菜单。
1)ncside、delside、incvet、delvet 函数:
分别用于增加边、删除边、增加结点和删除结点。
2)changemap 函数:
是图的操作主函数,允许用户进行图的修改操作。
3)compusinfor 函数:
用于显示所有景点的信息。
4)mainwork 函数:
是主要的用户界面函数,调用其他函数完成不同的操作。
5)main 函数:
是程序的入口点,调用 mainwork 函数开始执行程序。
3.逻辑设计
1.定义数据结构:
Side用于边的权值。
Vetinf用于表示图中的顶点信息。
Mapstr用于表示图的整体结构,包括顶点数组、邻接矩阵、顶点数量和边的数量等信息。
2.定义全局变量:
定义全局变量campus,用于存储校园结构的图。定义数组d[30]用于存储路径。定义数组visted[50]用于标记顶点是否被访问。定义了数组长度shortest[MAxNumber][MaxNumber]用于存储路径,用于重构最短路径。
3.图的初始化:
使用‘initmap()’函数初始化了一个校园的图,包括顶点和边的权值。
4.最短路径计算:
实现了floyd算法,计算了单源最短路径。
5.图的修改:
提供了增加、删除、和边的功能,以及修改节点和边信息的功能。通过用户登录验证实现了对图的修改操作。
6.其他功能:
提供了校园展示的功能。实现了弗洛伊德算法,计算任意两点之间的最短路径。
7.用户界面:
通过menu()函数实现一个简单的菜单界面,包含了不同功能的选项。通过‘mainwork()’函数实现了用户与系统的交互,根据用户的选择调用相应的功能函数。
8.主函数:
在main()函数中调用了‘main()’函数,启动了整个程序的执行。
四.实现代码(部分)
1)最短距离函数
int shortdistance(mapstr m) {
int i, j;//起始顶点和终点
printf("请输入要查询的两个景点的数字编号(用空格隔开)\n");
scanf("%d %d", &i, &j);
if (i < 0 || i > vertex || j < 0 || j > vertex) {//检测用户输入的编号是否在有效范围内
printf("输入信息有误!\n\n");//无效 输出提示
printf("请输入要查询的两个景点的数字编号(用空格隔开)\n");
scanf("%d %d", &i, &j);//重新输入
} else {
floyd(m);
display(m, i, j);
}
return 1;
}
使用floyd算法计算最短路径。
2)查询信息函数
int visit(mapstr m) {
//访问结点信息(读取景点信息)
int k;
printf("你想查看哪个景点的信息(1-15)?\n");
scanf("%d", &k);
if (k < 0 || k >= m.vetnum)
return 1;
printf("景点序号:%d\n", m.vets[k].number);
printf("景点名字:%s\n", m.vets[k].name);
printf("景点简介:%s\n", m.vets[k].intro);
return 0;
}用于查询节点信息
五.实现结果
六.心得总结
通过对各功能模块进行详细设计,并进行充分的测试,发现基本能达到预期的结果,功能也比较完善。但在功能上仍存在一定的不足。
这次课程设计巩固和加深了对数据结构的理解,提高了综合运用本课程的能力。培养了独立思考、深入研究、分析问题、解决问题的能力。
通过这次课程设计,让我学到很多,让我知道了认真上好专业课的重要性,以后多会在时间中锻炼自己,毕竟说和做还是有很大差距,而且写程序中要考虑周到,严密。
做本次课设非常的困难,以上两个对我来说都十分的艰难,编译过程中曾几度产生放弃的想法。但通过后来的学习以及咨询互联网有了浅显的了解。我深知此次代码我做的并不是一帆风顺也并不是十分完美,但是对哈希表和弗洛伊德算法有了大致的了解。提高了自己数据结构能力以及编写代码的能力。
七.参考文献
1)《C语言-----学校导游系统》 CSDN
2)《数据结构(C语言版)》 严蔚敏 吴伟民 清华大学出版社
3)《c程序设计(第三版)》 谭浩强 清华大学出版社
附录:(源代码)
哈希表
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TABLE_SIZE 40 // 哈希表长度
#define MAX_NAME_LEN 100 // 姓名最大长度
#define MAX_ADDRESS_LEN 200 // 家庭住址最大长度
// 学生信息结构体
typedef struct {
char name[MAX_NAME_LEN];
int student_number;
char phone_number[12];
char address[MAX_ADDRESS_LEN];
} Student;
// 哈希表节点结构体
typedef struct HashNode {
Student student;
struct HashNode *next;
} HashNode;
// 哈希表结构体
typedef struct {
HashNode *table[TABLE_SIZE];
} HashTable;
// 哈希函数
int hash(char *name) {
int sum = 0;//累加字符的as值
int i = 0;//索引
while (name[i] != '\0') {
sum += name[i];
i++;
}
return sum % TABLE_SIZE;
}
// 在哈希表中查找学生信息
int search(HashTable *hash_table, char *name, int *compares) {
int index = hash(name);
HashNode *current = hash_table->table[index];
*compares = 0;
while (current != NULL) {
(*compares)++;
if (strcmp(name, current->student.name) == 0) {
printf("学生姓名:%s\n学号:%d\n电话号码:%s\n家庭住址:%s\n", current->student.name,
current->student.student_number, current->student.phone_number, current->student.address);
return 1; // 学生信息找到
}
current = current->next;
}
return 0; // 学生信息未找到
}
// 向哈希表中插入学生信息
void insert(HashTable *hash_table, Student student) {
int index = hash(student.name);
HashNode *new_node = (HashNode *)malloc(sizeof(HashNode));
new_node->student = student;
new_node->next = NULL;
if (hash_table->table[index] == NULL) {
hash_table->table[index] = new_node;
} else {
HashNode *current = hash_table->table[index];
while (current->next != NULL) {
current = current->next;
}
current->next = new_node;
}
}
// 从哈希表中删除学生信息
int delete (HashTable *hash_table, char *name) {
int index = hash(name);
HashNode *current = hash_table->table[index];
HashNode *previous = NULL;
while (current != NULL) {
if (strcmp(name, current->student.name) == 0) {
if (previous == NULL) {
hash_table->table[index] = current->next;
} else {
previous->next = current->next;
}
free(current);
return 1; // 学生信息删除成功
}
previous = current;
current = current->next;
}
return 0; // 学生信息删除失败
}
// 修改哈希表中的学生信息
int modify(HashTable *hash_table, char *name, Student new_student) {
int index = hash(name);
HashNode *current = hash_table->table[index];
while (current != NULL) {
if (strcmp(name, current->student.name) == 0) {
strcpy(current->student.name, new_student.name);
current->student.student_number = new_student.student_number;
strcpy(current->student.phone_number, new_student.phone_number);
strcpy(current->student.address, new_student.address);
return 1; // 学生信息修改成功
}
current = current->next;
}
return 0; // 学生信息修改失败
}
// 从文件中读取初始班级的通讯录信息
void loadInitialData(HashTable *hash_table) {
FILE *file = fopen("student_data.txt", "r");
if (file == NULL) {
printf("无法打开文件。\n");
return;
}
char line[100];
while (fgets(line, sizeof(line), file)) {
Student student;
sscanf(line, "%s %d %s %s", student.name, &student.student_number, student.phone_number, student.address);
insert(hash_table, student);
}
fclose(file);
}
// 将当前哈希表中的学生信息保存到文件
void saveDataToFile(HashTable *hash_table) {
FILE *file = fopen("student_data.txt", "w");
if (file == NULL) {
printf("无法打开文件。\n");
return;
}
for (int i = 0; i < TABLE_SIZE; i++) {
HashNode *current = hash_table->table[i];
while (current != NULL) {
fprintf(file, "%s %d %s %s\n", current->student.name, current->student.student_number,
current->student.phone_number, current->student.address);
current = current->next;
}
}
fclose(file);
}
int main() {
HashTable hash_table;
for (int i = 0; i < TABLE_SIZE; i++) {
hash_table.table[i] = NULL;
}
loadInitialData(&hash_table);
char name[MAX_NAME_LEN];
int menu_choice;
while (1) {
printf("*------------------------------------*\n");
printf("*---------- 通讯录管理系统 ----------*\n");
printf("*--------- 1. 查询学生信息 ----------*\n");
printf("*--------- 2. 增加学生信息 ----------*\n");
printf("*--------- 3. 删除学生信息 ----------*\n");
printf("*--------- 4. 修改学生信息 ----------*\n");
printf("*--------- 5. 退出管理系统 ----------*\n");
printf("*------------------------------------*\n");
printf("请输入菜单编号:");
scanf("%d", &menu_choice);
getchar();
switch (menu_choice) {
case 1:
printf("请输入要查询的学生姓名:");
fgets(name, sizeof(name), stdin);
name[strcspn(name, "\n")] = '\0'; // 移除末尾的换行符
int compares;
if (search(&hash_table, name, &compares)) {
printf("比较的次数:%d\n", compares);
} else {
printf("未找到该学生信息。\n");
}
break;
case 2: {
Student new_student;
printf("请输入学生姓名:");
fgets(new_student.name, sizeof(new_student.name), stdin);
new_student.name[strcspn(new_student.name, "\n")] = '\0';
printf("请输入学号:");
scanf("%d", &(new_student.student_number));
getchar();
printf("请输入电话号码:");
fgets(new_student.phone_number, sizeof(new_student.phone_number), stdin);
new_student.phone_number[strcspn(new_student.phone_number, "\n")] = '\0';
printf("请输入家庭住址:");
fgets(new_student.address, sizeof(new_student.address), stdin);
new_student.address[strcspn(new_student.address, "\n")] = '\0';
insert(&hash_table, new_student);
printf("学生信息已添加。\n");
break;
}
case 3:
printf("请输入要删除的学生姓名:");
fgets(name, sizeof(name), stdin);
name[strcspn(name, "\n")] = '\0'; // 移除末尾的换行符
if (delete (&hash_table, name)) {
printf("学生信息已删除。\n");
} else {
printf("未找到该学生信息。\n");
}
break;
case 4: {
Student new_student;
printf("请输入要修改的学生姓名:");
fgets(name, sizeof(name), stdin);
name[strcspn(name, "\n")] = '\0'; // 移除末尾的换行符
printf("请输入新的学生姓名:");
fgets(new_student.name, sizeof(new_student.name), stdin);
new_student.name[strcspn(new_student.name, "\n")] = '\0';
printf("请输入新的学号:");
scanf("%d", &(new_student.student_number));
getchar();
printf("请输入新的电话号码:");
fgets(new_student.phone_number, sizeof(new_student.phone_number), stdin);
new_student.phone_number[strcspn(new_student.phone_number, "\n")] = '\0';
printf("请输入新的家庭住址:");
fgets(new_student.address, sizeof(new_student.address), stdin);
new_student.address[strcspn(new_student.address, "\n")] = '\0';
if (modify(&hash_table, name, new_student)) {
printf("学生信息已修改。\n");
} else {
printf("未找到该学生信息。\n");
}
break;
}
case 5:
saveDataToFile(&hash_table);
printf("程序已退出。\n");
return 0;
default:
printf("无效的菜单选择,请重新输入。\n");
break;
}
}
}
附录:(源代码)
学校导游系统
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Infinity 65535 //表示无穷大
#define MaxNumber 23 //用于邻接矩阵
#define vertex 15//顶点个数
typedef struct side { //边的权值
int wet;//权值
} side, wetmatrix[MaxNumber][MaxNumber]; //边的邻接矩阵类型
typedef struct vetinf { //顶点信息
int number;//顶点编号
char name[64];//顶点名称
char intro[256];//顶点介绍
} vetinf;
typedef struct mapstr { //图结构信息
vetinf vets[MaxNumber];//顶点数组
wetmatrix mat;//邻接矩阵
int vetnum, sidenum;
} mapstr;
//全局变量
mapstr campus;//图结构变量(学校校园)
int shortest[MaxNumber][MaxNumber];//定义全局变量存储最小路径
int pathh[MaxNumber][MaxNumber];//定义存储路径
//图的初始化
mapstr initmap() {
mapstr m;//构件图m
int i = 0, j = 0;
m.vetnum = 15;//定义顶点个数
m.sidenum = 25;//定义边的条数
for (i = 1; i <= vertex; i++)//依次设置顶点信息
m.vets[i].number = i;
//输入顶点信息
strcpy_s(m.vets[1].name, "正门");
strcpy_s(m.vets[1].intro, "福工学子的大门");
strcpy_s(m.vets[2].name, "二门");
strcpy_s(m.vets[2].intro, "福工学子的二门");
strcpy_s(m.vets[3].name, "第二教学楼");
strcpy_s(m.vets[3].intro, "福工学子的第二教学楼");
strcpy_s(m.vets[4].name, "图书馆");
strcpy_s(m.vets[4].intro, "福工学子的内卷场所");
strcpy_s(m.vets[5].name, "博达公寓");
strcpy_s(m.vets[5].intro, "福工学子的公寓");
strcpy_s(m.vets[6].name, "第一教学楼");
strcpy_s(m.vets[6].intro, "福工学子的教学楼");
strcpy_s(m.vets[7].name, "第五教学楼");
strcpy_s(m.vets[7].intro, "福工学子的教学楼");
strcpy_s(m.vets[8].name, "第六教学楼");
strcpy_s(m.vets[8].intro, "福工学子的教学楼");
strcpy_s(m.vets[9].name, "第十教学楼");
strcpy_s(m.vets[9].intro, "福工学子的教学楼");
strcpy_s(m.vets[10].name, "体育场1");
strcpy_s(m.vets[10].intro, "福工学子的运动场");
strcpy_s(m.vets[11].name, "第十教学楼");
strcpy_s(m.vets[11].intro, "福工学子的教学楼");
strcpy_s(m.vets[12].name, "音乐厅");
strcpy_s(m.vets[12].intro, "福工学子的音乐厅");
strcpy_s(m.vets[13].name, "体育场2");
strcpy_s(m.vets[13].intro, "福工学子的运动场");
strcpy_s(m.vets[14].name, "第四教学楼");
strcpy_s(m.vets[14].intro, "福工学子的教学楼");
strcpy_s(m.vets[15].name, "第三教学楼");
strcpy_s(m.vets[15].intro, "福工学子的教学楼");
for (i = 1; i <= vertex; i++)
for (j = 1; j <= vertex; j++)
m.mat[i][j].wet = Infinity;//初始化图的邻接矩阵
m.mat[1][0].wet = 280;
m.mat[1][2].wet = 170;
m.mat[1][3].wet = 120;
m.mat[3][4].wet = 60;
m.mat[2][5].wet = 150;
m.mat[3][4].wet = 260;
m.mat[3][6].wet = 180;
m.mat[5][6].wet = 260;
m.mat[5][8].wet = 180;
m.mat[6][7].wet = 50;
m.mat[7][8].wet = 30;
m.mat[7][11].wet = 235;
m.mat[8][9].wet = 60;
m.mat[8][10].wet = 150;
m.mat[10][11].wet = 140;
m.mat[11][12].wet = 70;
m.mat[12][13].wet = 400;
m.mat[13][14].wet = 150;
for (i = 1; i <= vertex; i++)//无向带权图是对称矩阵,给其另一半赋值
for (j = 1; j <= vertex; j++)
m.mat[j][i].wet = m.mat[i][j].wet;
return m;
}
int visit(mapstr m) {
//访问结点信息(读取景点信息)
int k;
printf("你想查看哪个景点的信息(1-15)?\n");
scanf("%d", &k);
if (k < 0 || k >= m.vetnum)
return 1;
printf("景点序号:%d\n", m.vets[k].number);
printf("景点名字:%s\n", m.vets[k].name);
printf("景点简介:%s\n", m.vets[k].intro);
return 0;
}
//查询景点在图中的序号
int locatevet(mapstr m, int v) {
int i;
for (i = 0; i <= m.vetnum; i++)
if (v == m.vets[i].number)
return i;//找到返回顶点i
return -1;//未找到
}
//弗洛伊德算法
void floyd(mapstr m) {//用于通过弗洛伊德算法实现了对图中各个顶点之间最短路径的计算
int i, j, k;
for (i = 1; i <= vertex; i++) {
for (j = 1; j <= vertex; j++) {
shortest[i][j] = m.mat[i][j].wet;//将图的邻接矩阵赋值给 shortest二维数组
pathh[i][j] = j;//将矩阵pathh全部初始化为顶点编号
}
}
for (k = 1; k <= vertex; k++) { //核心操作,完成了以k为中间点对所有的顶点对(i,j)进行检测和修改
for (i = 1; i <= vertex; i++) {
for (j = 1; j <= vertex; j++) {
if (shortest[i][j] > shortest[i][k] + shortest[k][j]) {//如果当前的最短路径大于通过中间点的路径的长度和
shortest[i][j] = shortest[i][k] + shortest[k][j];//更新最短路径的长度
pathh[i][j] = pathh[i][k];//记录通过中间点k的最短路径的前驱顶点
}
}
}
}
}
//输出最短路径
void display(mapstr m, int i, int j) {
int a, b;
a = i, b = j;//将起点赋值给a,终点赋值给b
printf("您要查询的两景点间最短路径:\n\n");
printf("%d%s", a, m.vets[a].name);
while (pathh[i][j] != b) {//使用pathh数组还原顶点i到j的最短路径
printf("-->%d%s", pathh[i][j], m.vets[pathh[i][j]].name);
i = pathh[i][j];//通过循环沿着路径输出编号和名称
}
printf("-->%d%s\n\n", b, m.vets[b].name);
printf("%s-->%s的最短路径是:%d米\n\n", m.vets[a].name, m.vets[b].name,
shortest[a][b]);//通过shortest数组获取最短路径的长度
}
//任意两点间距离
int shortdistance(mapstr m) {
int i, j;//起始顶点和终点
printf("请输入要查询的两个景点的数字编号(用空格隔开)\n");
scanf("%d %d", &i, &j);
if (i < 0 || i > vertex || j < 0 || j > vertex) {//检测用户输入的编号是否在有效范围内
printf("输入信息有误!\n\n");//无效 输出提示
printf("请输入要查询的两个景点的数字编号(用空格隔开)\n");
scanf("%d %d", &i, &j);//重新输入
} else {
floyd(m);
display(m, i, j);
}
return 1;
}
//主页
void menu() {
printf(" ┌──────────────────────────────────────────────────────┐\n");
printf(" │ smart guide system from FJUT │\n");
printf(" │ │\n");
printf(" │ 菜 单 选 择 │\n");
printf(" │ ┌-------------------------------------------------┐ │\n");
printf(" │ │ 1.主页 │ 2.查询景点间最短路径 │ │\n");
printf(" │ │-------------------------------------------------│ │\n");
printf(" │ │ 3.学校景点介绍 │ 4.更改图信息 │ │\n");
printf(" │ │-------------------------------------------------│ │\n");
printf(" │ │ 5.查询景点 │ 0.退出 │ │\n");
printf(" │ └-------------------------------------------------┘ │\n");
printf(" └──────────────────────────────────────────────────────┘\n");
}
//以下是修改图的相关信息
//增加边
int incside(mapstr *m) {//目的是允许用户输入一条边的起点,终点和权值,并将这条边添加到图的邻接矩阵中
int l, n, distance;//定义了用户输入的边起点终点和权值
printf("\n请输入边的起点和终点编号,权值:");
scanf("%d%d%d", &l, &n, &distance);
while (l < 0 || l > m->vetnum || n < 0 || n > m->vetnum) {//目的是检查用户输入的边的起点和终点是否在合法范围内
printf("输入错误,请重新输入");//如果不是则重新输入
scanf("%d %d", &l, &n);
}
if (locatevet(campus, l) < 0) {//目的检查边的起点是否在图中存在
printf("此节点%d已删除", l);//若不存在 输出提示
return 1;
}
if (locatevet(campus, n) < 0) {//目的检查边的终点是否存在
printf("此节点%d已被删除", n);//若不存在 输出提示
return 1;
}
m->mat[l][n].wet = distance;
m->mat[n][l].wet = m->mat[l][n].wet;//将用户输入的权值设置到邻接矩阵中边的权值
m->sidenum++;//图的边数+1
return 1;
}
//删除边
int delside(mapstr *m) {//目的允许用户输入一对顶点的编号,删除图中对应的边
int l, n, v0, v1;//l、n用于存储待测边的起点终点在图中的位置,v0、v1用于存储用户输入的待删除边的起点和终点
if (m->vetnum <= 0) {//如果图中没有顶点
printf("图中没有边了,无法删除");//输出提示
return 1;
}
printf("\n下面请输入您要删除的边的起点和终点编号:");
scanf("%d %d", &v0, &v1);//存入用户输入的待删除的起点和终点
l = locatevet(campus, v0);//调用locatevet函数寻找边的起点在图中的位置存入I中
if (m->vetnum < 0) {//如果返回值<0
printf("此%d顶点已删除", v0);//说明起点不存在 输出提示
return 1;
}
n = locatevet(campus, v1);//调用locatevet函数寻找边的终点点在图中的位置存入n中
if (n < 0) {//如果返回值<0
printf("此%d顶点已删除", v1);//说明终点不存在 输出提示
return 1;
}
//因为是无向图
m->mat[l][n].wet = Infinity;
m->mat[n][l].wet = Infinity;//将删掉的边的权值改为无穷
m->sidenum--;//边数-1
return 1;
}
//增加结点
int incvet(mapstr *m) {//目的允许用户一个新的顶点信息并添加到顶点数组中
int i;//用于循环计数
m->vetnum++;//顶点数加一 表示增加一个新的结点
printf("请输入您要增加结点的信息:");
printf("\n编号:");
scanf("%d", &m->vets[m->vetnum].number);//将用户输入的编号存入
printf("名称:");
scanf("%s", m->vets[m->vetnum].name, 10);//将用户输入的名字存入
printf("简介:");
scanf("%s", m->vets[m->vetnum].intro, 20);//将用户输入的介绍存入
for (i = 1; i <= m->vetnum; i++) {//使用循环将新增的顶点与其他顶点之间的权值初始化为无穷大
m->mat[m->vetnum][i].wet = Infinity;
m->mat[i][m->vetnum].wet = Infinity;
}
return 1;
}
//删除结点
int delvet(mapstr *m) {//目的是允许用户输入一个待删除的顶点编号,然后删除图中对应顶点及相关的边
int i = 0, j, l, v;//i、j用于循环计数、l用于存储删除结点的位置、v用于存储用户输入的待删除序号
if (m->vetnum <= 0) {//如果图中没有顶点
printf("图中已无顶点");//输出提示
return 1;
}
printf("\n下面请输入您要删除的景点编号:");
scanf("%d", &v);//用户输入待删除的编号存入
while (v < 0 || v > m->vetnum) {//循环检查输入的顶点编号是否有效
printf("\n输入错误!请重新输入:");//无效 输出提示
scanf("%d", &v);
}
l = locatevet(campus, v);//调用locatevet函数 目的找到顶点在图中的位置
if (l < 0) {//如果函数返回值<0
printf("顶点%d已删除\n", v);//输出提示
return 1;
}
for (i = l; i <= m->vetnum - 1; i++)
for (j = 1; j <= m->vetnum; j++)
m->mat[i][j] = m->mat[i + 1][j];//将二维数组中的第m+1行依次向前移动一行
for (i = l; i <= m->vetnum - 1; i++)
for (j = 1; j <= m->vetnum; j++)
m->mat[j][i] = m->mat[j][i + 1];//将二维数组中的第m+1列依次向前移动一列
m->vets[v].number = -1;//标记删除节点,后期打印也不会显示该点
m->vetnum--;//顶点-1
return 1;
}
//图的操作主函数
int changemap(mapstr *m) {
int choice;
printf(" ┌─────────────────────────────┐ \n");
printf(" │ (1)增加结点 │ (2)删除结点 │ \n");
printf(" │──────────────│──────────────│ \n");
printf(" │ (3)删除边 │ (4) 增加边 │ \n");
printf(" │──────────────│──────────────│ \n");
printf(" │ (5)返回主页 │ │ \n");
printf(" └─────────────────────────────┘ \n");
do {
printf("\n请输入你的选择:");
scanf("%d", &choice);
switch (choice) {
case 1:
incvet(m);
break;
case 2:
delvet(m);
break;
case 3:
delside(m);
break;
case 4:
incside(m);
break;
case 5:
system("cls");
menu();
return 1;
default:
printf("未找到该功能,请输入有效选项!\n");
break;
}
} while (choice);
}
//显示所有景点信息
void compusinfor(mapstr m) {
int i;
printf(" \n\n编号 景点名称 简介\n");
printf("****************************************************************************\n");
for (i = 1; i <= m.vetnum; i++) {
if (m.vets[i].number != -1)//检测是否存在 -1为不存在
printf("%-10d%-40s%-70s\n", m.vets[i].number, m.vets[i].name, m.vets[i].intro);
}
printf("****************************************************************************\n");
}
//用户界面
void mainwork() {
menu();
int choice;
campus = initmap();
do {
printf("请输入你的选择:");
scanf("%d", &choice);
switch (choice) {
case 1:
system("cls");
menu();
break;
case 2:
system("cls");
shortdistance(campus);
break;
case 3:
system("cls");
compusinfor(campus);
break;
case 4:
system("cls");
changemap(&campus);
break;
case 5:
system("cls");
visit(campus);
break;
case 0:
system("cls");
printf("谢谢使用\n");
break;
default:
printf("未找到该功能,请输入有效选项!\n");
break;
}
} while (choice);
}
int main() {
mainwork();
return 0;
}