#include "./includes/link.h" // 节点交换 void swap(STU *a, STU *b) { // 方法一: 直接通过临时指针交换数据 // STU *tp = (STU *)malloc(sizeof(STU)); // tp->sid = a->sid; // strcpy(tp->name, a->name); // tp->age = a->age; // a->sid = b->sid; // strcpy(a->name, b->name); // a->age = b->age; // b->sid = tp->sid; // strcpy(b->name, tp->name); // b->age = tp->age; // free(tp); // 释放临时指针 // 方法二: 通过结构体指针交换数据 // 先换数据 STU *tp = (STU *)malloc(sizeof(STU)); *tp = *a; *a = *b; *b = *tp; // 再换指针的下一个节点指向 tp->next = a->next; a->next = b->next; b->next = tp->next; free(tp); // 释放临时指针 } // 添加学生 STU *insert_stu(STU *head, STU *item) { // 如果头指针为空,说明链表为空,直接将新建的节点作为头指针 if (head == NULL) { head = item; } else { // 如果头指针不为空,说明链表不为空,需要找到链表的尾部,将新建的节点插入到尾部 STU *p = head; // 创建一个临时指针,用来保存头指针,防止头指针丢失 while (p->next != NULL) // 当 p 的下一个节点不为空时,说明 p 不是尾部 { p = p->next; // p 一直向后移动,直到找到尾部 } p->next = item; // 将 item 插入到尾部 } return head; // 返回头指针 } // 删除学生 STU *delete_stu(STU *head, char sid) { if (NULL == head) { printf("\033[%dm无数据,无法删除\033[0m\n", RED); return NULL; } if (head->sid == sid) { // *tp 为临时指针,用来保存头指针 STU *tp = head; // 保存头指针 head = head->next; // 头指针指向下一个节点 free(tp); // 释放头指针 } else { STU *tp = head; // 保存头指针 while (NULL != tp->next && sid != tp->next->sid) // 当 tp 的下一个节点不为空时,且下一个节点的学号不等于要删除的学号时,继续向后移动指针 tp { tp = tp->next; // tp 一直向后移动,直到找到尾部 } if (NULL == tp->next) // 到达了链表尾部还是没有找到要删除的内容 { printf("\033[%dm未找到 sid = %d 的学生,删除失败\033[0m\n", RED, sid); } else { STU *tpx = tp->next; // 保存要删除的节点 tp->next = tpx->next; // 将链表位置指向要删除的节点的下一个节点 free(tpx); // 释放要删除的节点 printf("\033[%dm删除 sid = %d 的学生成功\033[0m\n", BLUE, sid); } } return head; // 返回头指针 } // 打印学生 void shows(STU *head) { // 如果头指针为空,说明链表为空,直接返回 if (head == NULL) { printf("\033[%dm无数据,无法打印\033[0m\n", RED); return; } else { // 如果头指针不为空,说明链表不为空,需要遍历链表,打印每一个节点的信息 STU *p = head; // 创建一个临时指针,用来保存头指针,防止头指针丢失 // 清屏 // printf("\033[2J"); printf("--------------------\n"); printf("\033[%dm学号\t姓名\t年龄\033[0m\n", BLUE_B); while (p != NULL) // 当 p 不为空时,说明 p 不是尾部,则打印 p 的信息 { printf("\033[%dm%d\t%s\t%d\033[0m\n", BLUE, p->sid, p->name, p->age); // 打印 p 的信息 p = p->next; // p 一直向后移动,直到找到尾部 } } } // 查询学生 STU *find_stu(STU *head, char sid) { if (NULL == head) { printf("\033[%dm无数据,无法查询\033[0m\n", RED); return NULL; // 返回空指针,说明没有找到要查询的节点 } STU *tp = head; // 保存头指针 while (NULL != tp->next && sid != tp->sid) // 当 tp 的下一个节点不为空时,且下一个节点的学号不等于要查询的学号时,继续向后移动指针 tp { tp = tp->next; // tp 一直向后移动,直到找到尾部 } if (sid == tp->sid) return tp; // 返回当前节点的指针,即为要查询的节点 return NULL; // 返回空指针,说明没有找到要查询的节点 } // 修改学生 STU *update_stu(STU *head, char sid) { if (NULL == head) { printf("\033[%dm无数据,无法修改\033[0m\n", RED); return NULL; } STU *tp = find_stu(head, sid); // 查找要修改的节点 if (tp == NULL) { printf("\033[%dm未找到 sid = %d 的学生\033[0m\n", RED, sid); } else { // 临时变量,用来保存要修改的学生信息 char msid; char mname[32]; int mage; // 输入要修改的内容 printf("输入要修改的学生的 学号 姓名 年龄: "); scanf("%hhd %s %d", &msid, mname, &mage); tp->sid = msid; strcpy(tp->name, mname); tp->age = mage; // 修改成功 printf("\033[%dm修改 sid = %d 的学生成功\033[0m\n", BLUE, sid); } return head; // 返回头指针 } // 排序学生(选择排序,也可以使用归并排序) STU *sort_stu(STU *head) { if (NULL == head) { printf("\033[%dm无数据,无法排序\033[0m\n", RED); return NULL; } // 选择排序方式: 1) 按照sid 2)按照age int choose; // 选择排序方式 printf("选择排序方式: 1) 按照sid 2)按照age: "); scanf("%d", &choose); if (1 == choose) // 按照sid排序 { // 选择排序方式: 1) 升序 2)降序 int choose2; // 选择排序方式 printf("选择排序方式: 1) 升序 2)降序: "); scanf("%d", &choose2); if (1 == choose2) // 升序 { // 按照sid升序排序 STU *p = head; // 创建一个临时指针,用来保存头指针,防止头指针丢失 while (p != NULL) // 相当于 for (int i = 0; i < len; i++) { STU *q = p->next; // 创建一个临时指针,用来保存头指针,防止头指针丢失 while (q != NULL) // 相当于 for (int j = i + 1; j < len; j++) { if (p->sid > q->sid) // 相当于 if (arr[i] > arr[j]) { swap(p, q); // 相当于 swap(arr[i], arr[j] } q = q->next; } p = p->next; } } else if (2 == choose2) // 降序 { STU *p = head; // 创建一个临时指针,用来保存头指针,防止头指针丢失 while (p != NULL) { STU *q = p->next; // 创建一个临时指针,用来保存头指针,防止头指针丢失 while (q != NULL) { if (p->sid < q->sid) { swap(p, q); } q = q->next; } p = p->next; } } else { // 选择错误 printf("\033[%dm选择错误\033[0m\n", RED); } } else if (2 == choose) // 按照age排序 { // 选择排序方式: 1) 升序 2)降序 int choose2; // 选择排序方式 printf("选择排序方式: 1) 升序 2)降序: "); scanf("%d", &choose2); if (1 == choose2) // 升序 { STU *p = head; // 创建一个临时指针,用来保存头指针,防止头指针丢失 while (NULL != p) { STU *q = p->next; // 创建一个临时指针,用来保存头指针,防止头指针丢失 while (NULL != q) { if (p->age > q->age) { swap(p, q); } q = q->next; } p = p->next; } } else if (2 == choose2) // 降序 { STU *p = head; // 创建一个临时指针,用来保存头指针,防止头指针丢失 while (NULL != p) { STU *q = p->next; // 创建一个临时指针,用来保存头指针,防止头指针丢失 while (NULL != q) { if (p->age < q->age) { swap(p, q); } q = q->next; } p = p->next; } } else { // 选择错误 printf("\033[%dm选择错误\033[0m\n", RED); } } else { // 选择错误 printf("\033[%dm选择错误\033[0m\n", RED); } return head; // 返回头指针 } // 反序学生(头变尾,尾变头) STU *reverse_stu(STU *head) { if (NULL == head) { printf("\033[%dm无数据,无法反序\033[0m\n", RED); return NULL; } STU *pi = head->next; // 当前节点的下一个节点地址 head->next = NULL; // 把头节点的下一个节点置空,让头节点成为尾节点 STU *tp = NULL; // 当前节点的下一个节点地址 pi 的下一个节点地址 while (pi != NULL) // 当 pi 不为空时,说明 pi 不是尾部,则反序 pi 的信息 { tp = pi->next; // 首先 tp 保存 pi 的下一个节点地址 pi->next = head; // 然后把 pi 的下一个节点地址指向 head head = pi; // 然后让 hand 指向 pi pi = tp; // 最后找到新的 pi,即 tp } printf("\033[%dm反序成功\033[0m\n", BLUE); return head; // 返回头指针 } // 保存数据到文件 void saveToFile(STU *head, const char *filename) { FILE *file = fopen(filename, "w"); if (file == NULL) { printf("\033[%dm无法打开文件, 保存数据失败\033[0m", RED); return; } STU *current = head; // 定义临时指针,用来保存头指针,防止头指针丢失 while (current != NULL) // 当 p 不为空时,说明 p 不是尾部,则打印 p 的信息 { // 格式化存储数据,每个字段之间用逗号分隔 fprintf(file, "%d,%s,%d\n", current->sid, current->name, current->age); current = current->next; // p 一直向后移动,直到找到尾部 } printf("保存数据成功\n"); fclose(file); } // 从文件中读取数据 STU *readFromFile(const char *filename) { FILE *file = fopen(filename, "r"); if (file == NULL) { printf("\033[%dm无法打开文件, 加载数据失败\033[0m", RED); return NULL; } STU *head = NULL; // 定义头指针 STU *prev = NULL; // 定义尾指针 char line[100]; // 100 个字符的数组,用来保存每一行的数据 while (fgets(line, sizeof(line), file) != NULL) // 逐行读取文件内容 { STU *node = (STU *)malloc(sizeof(STU)); // 申请一个节点的内存, 用来保存学生信息 /* %d :表示读取一个整数,并将其存储在 &(node->sid)的地址中,即学生的学号。 , :表示读取一个逗号,用于分隔学号和姓名之间的字段。 %[^,] :表示读取一个字符串,直到遇到逗号为止,并将其存储在 node -> name 中,即学生的姓名。[^,] 表示匹配除逗号以外的任意字符。 , :表示读取一个逗号,用于分隔姓名和年龄之间的字段。 %d :表示读取一个整数,并将其存储在 &(node->age)的地址中,即学生的年龄。 */ sscanf(line, "%d,%[^,],%d\n", &(node->sid), node->name, &(node->age)); // 将每一行的数据保存到节点中 // fscanf() 每次执行后会丢失数据,所以需要使用 sscanf() 函数,原因是 fscanf() 函数会在每次执行后将文件指针向后移动,而 sscanf() 函数不会移动文件指针。 // fscanf(file, "%d,%[^,],%d", &(node->sid), node->name, &(node->age)); // 将每一行的数据保存到节点中 node->next = NULL; // 新建的节点的下一个节点指向空,因为是尾部插入 if (head == NULL) // 当头指针为空时,说明链表为空,直接将新建的节点作为头指针 { head = node; } else // 否则,说明链表不为空,需要找到链表的尾部,将新建的节点插入到尾部 { prev->next = node; // 将新建的节点插入到尾部 } prev = node; // 将尾指针指向新建的节点 } fclose(file); printf("加载数据成功\n"); return head; }