03学生管理系统(链表)

预处理

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

#define STUDENT_NUMBER_MAX 50
#define STUDENT_NAME_SEX_MAX 20
#define STUDENT_SEX_MAX 5
#define SUBJECT_NUMBER 2

菜单

//目录 
void menu()
{
    printf("\n\n\n");
    printf("\t\t-------------------------------------------------\n");
    printf("\t\t||              ----------------               ||\n");
    printf("\t\t||**************学生信息管理系统(链表版)*******||\n");
    printf("\t\t||              ----------------               ||\n");
    printf("\t\t||                                             ||\n");
    printf("\t\t||~~~~~~~~~~~~~~~1.录入学生信息~~~~~~~~~~~~~~~~||\n");
    printf("\t\t||~~~~~~~~~~~~~~~2.删除学生信息~~~~~~~~~~~~~~~~||\n");
    printf("\t\t||~~~~~~~~~~~~~~~3.修改学生信息~~~~~~~~~~~~~~~~||\n");
    printf("\t\t||~~~~~~~~~~~~~~~4.查询学生信息~~~~~~~~~~~~~~~~||\n");
    printf("\t\t||~~~~~~~~~~~~~~~5.显示学生信息~~~~~~~~~~~~~~~~||\n");
    printf("\t\t||~~~~~~~~~~~~~~~7.依平均分排序~~~~~~~~~~~~~~~~||\n");
    printf("\t\t||~~~~~~~~~~~~~~~8.清屏~~~~~~~~~~~~~~~~~~~~~~~~||\n");
    printf("\t\t||~~~~~~~~~~~~~~~9.退出~~~~~~~~~~~~~~~~~~~~~~~~||\n");
    printf("\t\t||                                             ||\n");
    printf("\t\t||*********************************************||\n");
}

构造学生结构体,构造链表

typedef struct student
{
    int num;
    char name[STUDENT_NAME_SEX_MAX];
    char clas[STUDENT_NAME_SEX_MAX];
    char sex[STUDENT_SEX_MAX];
    float Eng_score;
    float C_score;
    float total;
    float aver;
}student;

//构造链表,数据域为学生信息
typedef struct Node
{
    student stu;
    struct Node* next;
}Node;

功能实现

Node* initList();//初始化链表
void addStudent(Node* list);//录入
void showStudent(Node* list);//显示
void sortByAverage(Node* list);//排序(平均分)
void searchStudent(Node* list);//查找
void deleteStudent(Node* list);//删除
void modifyStudent(Node* list);//修改

主函数

int main()
{
    int input;

    Node* list = initList();//头节点指针

    do
    {
        menu();
        printf("请输入:");
        scanf("%d", &input);
        getchar();
        switch (input)
        {
        case 9:
            printf("已退出!\n");//退出
            exit(1);
        case 1:
            addStudent(list);                    //添加学生信息
            break;
        case 2:
            deleteStudent(list);                 //删除学生信息
            break;
        case 3:
            modifyStudent(list);                 //修改学生信息
            break;
        case 4:
            searchStudent(list);                 //查找学生信息
            break;
        case 5:
            showStudent(list);                   //显示学生信息
            break;
        case 7:
            sortByAverage(list);                 //依平均分排序
            break;
        case 8:
            system("clear");                     //清屏
            break;
        default:
            printf("输入指令有误!\n");
            break;
        }
    } while (input);
    return 0;
}

初始化

//初始化(带哑节点)
Node* initList() {
    Node* p = (Node*)malloc(sizeof(Node)); // 分配内存空间
    if (p == NULL) {
        // 处理内存分配失败的情况
        perror("Failed to allocate memory for the dummy node");
        exit(EXIT_FAILURE);
    }
    memset(p, 0, sizeof(Node)); // 清零整个节点
    p->next = NULL; // 确保哑节点的next指针设置为NULL
    return p; // 返回哑节点指针
}

添加(头插法)

//头插法添加
void addStudent(Node* list)
{
    Node* node = (Node*)malloc(sizeof(Node));
    if (!node) {
        printf("内存分配失败!\n");
        return;
}
    memset(&node->stu, 0, sizeof(student));//将节点的学生信息清零

    printf("请输入学号:");
    scanf("%d", &node->stu.num);
    printf("请输入姓名:");
    scanf("%s", node->stu.name);
    printf("请输入班级:");
    scanf("%s", node->stu.clas);
    printf("请输入性别:");
    scanf("%s", node->stu.sex);
    printf("请输入英语成绩:");
    scanf("%f", &node->stu.Eng_score);
    printf("请输入c语言成绩:");
    scanf("%f", &node->stu.C_score);

    // 插入节点到链表头部
    node->next = list->next; // 新节点的next指向原链表的第一个节点
    list->next = node;        // 链表头的next指向新节点

    printf("插入成功!\n");
}

删除

// 删除
void deleteStudent(Node* list)
{
    // 判断头节点后面是否有节点
    if (list->next == NULL)
    {
        printf("没有信息可删除!\n");
        return;
    }
    // 删除需要两个指针,一个指向前一个,一个指向当前
    Node* pre = list;
    Node* current = list->next;
    int num;
    printf("请输入要删除的学号:");
    scanf("%d", &num);
    getchar();

    //在单向链表中,遍历的方向是从链表的头部开始,沿着 next 指针向链表的尾部移动。
    // 这是因为单向链表只提供了向前的链接,没有向后的链接。
    while (current)//当前节点不为空
    {
        if (current->stu.num == num)
        {
         /*   NULL(头) < -pre < -current < -next_node < -...(尾)
                v                v
                + ---- - ++---- - +
                | stu |        | stu |
                | ... | -----> | ... |
                +---- - ++---- - +                */
            pre->next = current->next;
            free(current);//参数是一个指针
            current = NULL;//避免野指针
            printf("已删除!\n");
            return;
        }
        pre = current;
        current = current->next;

    }
    printf("学生信息不存在!\n");
}

修改

// 修改
void modifyStudent(Node* list)
{
    // 判断头节点后面是否有节点
    if (list->next == NULL)
    {
        printf("没有信息可修改!\n");
        return;
    }
    int num;
    printf("请输入要修改的学号:");
    scanf("%d", &num);
    getchar();
    list = list->next;
    while (list)
    {
        if (list->stu.num == num)
        {
            printf("请输入姓名:");
            scanf("%s", list->stu.name);
            printf("请输入班级:");
            scanf("%d", &list->stu.clas);
            printf("请输入性别:");
            scanf("%d", &list->stu.sex);
            printf("请输入英语成绩:");
            scanf("%f", &list->stu.Eng_score);
            printf("请输入c语言成绩:");
            scanf("%f", &list->stu.C_score);
            printf("修改成功!\n");
            return;
        }
        list = list->next;
    }
    printf("学生信息不存在!\n");
}

查找

// 查找学生信息
void searchStudent(Node* list)
{
    // 判断头节点后面是否有节点
    if (list->next == NULL)
    {
        printf("没有信息可查询!\n");
        return;
    }

    int num;
    printf("请输入要要查找的学号:");
    scanf("%d", &num);
    getchar();
    list = list->next;
    while (list)
    {
        if (num == list->stu.num)
        {
            printf("\n");
            printf("学号: %d, 姓名: %s, 班级: %s, 性别: %s, 英语: %.2f, C语言: %.2f, 平均分: %.2f\n",
                list->stu.num, list->stu.name, list->stu.clas, list->stu.sex,
                list->stu.Eng_score, list->stu.C_score, list->stu.aver);
            printf("\n");
            return;
        }
        list = list->next;
    }
    printf("学生信息不存在!\n");
}

显示

// 展示学生信息
void showStudent(Node* list) {
    if (list == NULL) return; // 确保list不是NULL
    // 跳过可能的哑节点(如果存在)
    Node* current = list->next;
    if (current == NULL) return; // 确保链表不为空

    printf("\n");
    while (current) {
        printf("学号: %d, 姓名: %s, 班级: %s, 性别: %s, 英语: %.2f, C语言: %.2f, 平均分: %.2f\n",
            current->stu.num, current->stu.name, current->stu.clas, current->stu.sex,
            current->stu.Eng_score, current->stu.C_score, (current->stu.Eng_score + current->stu.C_score) / 2.00);
        current = current->next;
    }
    printf("\n");
}

排序(冒泡降序)

// 冒泡排序,根据学生的平均分降序排序链表
void sortByAverage(Node* head) {
    if (head == NULL || head->next == NULL) {
        printf("没有成绩可排序!\n");
        return;
    }
    //遍历链表计算每个节点的平均分
    Node* current = head->next;
    // 跳过哑节点
    while (current!=NULL ) {
        current->stu.aver = (current->stu.Eng_score + current->stu.C_score) / 2.0;
        printf("%.2f\n",current->stu.aver);
        current = current->next;
    }

    bool swapped;
    do {
        swapped = false; // 每次开始新的一轮排序前,设置swapped为false
        Node* current = head->next; // 哑节点后的首个数据节点

        // 开始冒泡排序
        while (current != NULL && current->next != NULL) {
            Node* nextNode = current->next;

            // 比较当前节点和下一个节点的平均分
            if (current->stu.aver < nextNode->stu.aver) {
                // 发生交换,更新swapped标记
                swapped = true;

                // 交换两个节点的Student信息
                student tempStu = current->stu;
                current->stu = nextNode->stu;
                nextNode->stu = tempStu;

                // 交换完成,将current指针前移一位
                current = nextNode;
            }

            // 移动到下一个节点
            current = current->next;
        }
    } while (swapped); // 如果进行了交换,继续进行下一轮排序

    // 冒泡排序结束后,swapped标志位会停止循环,此时current指针应该在最后一个节点上
 // 我们需要从head的下一个节点开始打印,即从头节点的下一个节点开始遍历整个链表
    current = head->next; // 从头节点的下一个节点开始

    // 打印排序后的链表
    printf("排序后的链表:\n");
    while (current != NULL) { // 确保current不是NULL
        printf("学号: %d, 姓名: %s, 班级: %s, 性别: %s, 英语: %.2f, C语言: %.2f, 平均分: %.2f\n",
            current->stu.num, current->stu.name, current->stu.clas, current->stu.sex,
            current->stu.Eng_score, current->stu.C_score, current->stu.aver); // 使用已计算的平均分
        current = current->next; // 移动到下一个节点
    }
    printf("\n");
}

&

在C语言中,如果使用 scanf 来读取整数到结构体成员时,确实需要使用 & 操作符来获取成员的地址。这是因为 node->stu.num 是一个整数,而不是一个指针。& 操作符用于获取变量的内存地址,以便 scanf 可以正确地将输入的整数值存储到该地址处。

以下是正确的 scanf 使用示例:

scanf("%d", &node->stu.clas);  // 正确,因为clas是整数
scanf("%d", &node->stu.sex);   // 正确,因为sex是整数
scanf("%d", &node->stu.Eng_score); // 正确,因为Eng_score是整数
scanf("%d", &node->stu.C_score);   // 正确,因为C_score是整数

对于字符串类型的成员,比如 node->stu.num 如果它实际上是一个字符串(尽管通常学号是一个整数),使用 %s 格式符时,不需要 & 操作符,因为数组名本身就是地址。但是,如果 node->stu.num 是一个字符数组用来存储学号(通常是一个字符串),并且你想要读取一个字符串,应该这样写:

scanf("%s", node->stu.num); // 正确,如果num是一个字符数组
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值