c入门第二十二篇: 学生成绩管理系统查询优化(二分查找)

前言

师弟: “经过几轮优化之后,我的学生成绩管理系统,感觉已经非常不错了,是我学习以来做的最好的系统了。没想到,还是被嘲笑了。”
我:“怎么被嘲笑了?”
师弟:“程夏她说: 你在录入学生的时候,id是增序录入的,在查询的时候,就不能高效一点查询,不去遍历整个数据?”
我:“确定这个女生不是我们计算机系的?”
师弟:“的确不是我们计算机系的呢。”
我:“那人家学习的速度和你相比,像是龟兔赛跑呀。”
看师弟有点难受,我:“干嘛这么惆怅,谁笑的开心还不一定呢。在讲二分查找之前,讲个故事吧。”

龟兔赛跑

乌龟小龟情不自禁的喜欢上了兔子小兔,然后就展开自己快速的步伐,去追小兔,可是小兔的速度太快了,小龟怎么也追不上,小龟爬啊爬,爬啊爬,历经了千山万水,日升日落,春去秋来,小兔的影子都没有看到。
这一天黄昏时分,小龟累了,停留了一下脚步。这个时候,金黄的阳光照射过来,绚丽而灿烂,小龟沉醉在其中,忘记了心中所想,感觉身上满满的都是幸福,闭上了眼睛,尽情的享受着这份快乐。
“你放弃吧。”小龟被一声话语惊醒,本来很生气的小龟,瞬间变得不生气了,替换的更多的是内心的喜悦。因为说话的是小兔,自己满心喜欢和期待的小兔啊。
小龟倔强的回道:“不,我绝不放弃。”但是坚定的说完了以后,小龟内心产生了一个疑问。“真的是决不放弃吗?我能够追的上吗?”疑问很快就被小龟否定了。
“那好吧,你那么笨,既然想追,你就追吧,只要你能够追上我的步伐,那我就可以考虑和你在一起。”说完以后,还没等小龟反应,就已经绝尘而去。
看着小兔远去的背影,夹杂着激起的灰尘,小龟喜中也参含着忧。高兴的是自己有机会,不开心的是自己似乎又没有机会。
小龟重新整理了一下背上的行囊,上路了。目标很明确:追上小兔。
这一年在风吹雨打中过去了。
小龟犹豫了。“我真的能够追上小兔吗?就我这种速度,就我这么笨拙。或许我根本就不适合。或许我该重新认识一下现实,接受现实。”
就在小龟自我否定的时候,不知不觉来到了一个寺庙的门口。门口坐着两个和尚。一个和尚胖胖的,满脸的赘肉,看似很富裕的样子。另一个和尚,有点沧桑,略显消瘦,但是精神饱满。胖和尚惊讶的听着瘦和尚讲述着他去南海的经历。
原来一年前,瘦和尚说道:自己想去南海看看。胖和尚就笑话他,说自己多年前就开始攒钱,常常想着买船,顺江而下,去南海游历一番,可是没有实现,你这么穷,凭什么啊。没想到的是,这个瘦和尚竟然从南海游历回来了。胖和尚没有成功,瘦和尚竟然成功了。
小龟看着瘦和尚的面庞,不知道为什么感觉大受鼓舞,向他拜了拜。“天下事有难易乎?为之则难者亦易矣,不为则易者亦难矣。”小龟轻快的唱着歌就又重新出发了,扫去了之前的不快。
又是一年,伴随着风雨度过。
看着北归的大雁,小龟不知不觉中牵动了归家的情思,眼泪不自觉的流落了下来。
“枯藤老树昏鸦,小桥流水人家,古道西风瘦马,夕阳西下,断肠人在天涯。”一阵悲怆的诗歌从远处被风吹了过来。小龟沿着声音传来的方向看过去,只见一个身形消瘦的长衫游客,牵着一匹瘦瘦的马。不知道为什么小龟内心中很是凄凉。竟然不自觉的在这里放声大哭起来,很是想念在堂的老龟父母。
父母在,不远游。
“归去来兮!”小龟擦干了眼泪,看着夕阳西下,转过了身,朝家的方向爬去。
归家的路好长啊!小龟没有想到这些年竟然走了这么多的路。
终于,到了一条水边,一条熟悉的水边,小的时候曾经来过的地方。站在水边的高处,可以遥遥望见家乡的方向。
不知道为什么,前段时间日夜紧赶,就是想早点回家,这会快要到家了,反而有些羞涩。难道这是“近乡情更怯”。想想自己当年为爱而行,何尝不是在家乡闹得沸沸扬扬。
小龟长舒了一口气,收回了视线,又踏上了回家的路程。
归家途中经过一片农田,在田边不远处有一个老农,手中攥着一个东西,在焦急的等待,眼神紧紧的盯着田边的一棵树桩。
小龟好奇的走过去,问道:“大伯,你这是在等什么吗?”看清了大伯手中的护手后,小龟心头不禁一震。因为这是小龟在几年前送给小兔的生日礼物,这个护手上面有小兔的名字,他一眼就能够认出。为什么小兔的东西会在这儿呢?还没等大伯回答,小龟继续问道:“大伯,你这手中的手环是哪买的啊?好漂亮啊,我也想买一个。”
大伯晃了晃手中的手环,回道:“你说这个啊,不是我买的,是几天前的那个兔子的。”
小龟心头一紧,“前几天的兔子?她怎么了吗?”
大伯笑道:“她跑的太快,一下子撞死在了这棵树桩上了。瞧,血迹还在这儿呢。我在这儿继续等待,看看还有没有兔子过来,那样的话,我也就不用种庄稼了。”
小龟不知道为什么有点眩晕,终于抑制不住伤心,放声大哭了起来。伤心不止,眼泪顺着眼角就流了出来。
大伯看着大哭的乌龟,感觉默名其妙,转身就走了,嘀咕道:“今天,看样子是没有什么收获了,还是回去吧。”
大伯离开以后,小龟哭的更加伤心了,泪眼模糊中似乎看见了小兔的影子,他不顾一切的冲了上去,一把抱住了她,哭喊着说道:“小兔,你为什么,为什么,为什么这么早就离开了。”
“小龟,你这是干什么?”小兔说道,心中说不出的开心。
小龟听到了这话还没有反应过来,擦了擦眼泪。看了看眼前的小兔,不敢相信自己的眼睛。
“你还活着?”小龟兴奋的叫道。
“我当然还活着了,不然你以为呢!”小兔说道。“那滩血迹,是偷我护手的兔子的。走吧,我们回家。回去给你讲这个故事。”
“回家?”小龟疑惑地说道。
“不是说了嘛,只要你追上我,我就和你在一起。你怎么还是这么笨!”小兔说道。
小龟和小兔幸福的回家了。

二分查找

二分查找是一种在有序数组中查找特定元素的算法,其基本思想是将待查找区间分为两部分,每一步比较待查找元素与区间中间元素的大小,从而将搜索范围缩小到一半,又被称为折半查找。

/**
 * @arr: 有序数组
 * @l: 待查找区间左端点
 * @r: 待查找区间右端点
 * @x: 需要查找的元素
 */
int binary_search(int *arr, int l, int r, int x) {
    while (l <= r) {
        // 计算中间位置
        int m = l + (r - l) / 2; // 防止(l+r)直接相加导致的溢出

        // 检查x是否存在于中间位置
        if (arr[m] == x)
            return m;

        // 若x大,则忽略左半部分
        if (arr[m] < x) {
            l = m + 1;
        } else {
            // 若x小,则忽略右半部分
            r = m - 1;
    }

    // 若未找到元素,返回-1
    return -1;
}

完整代码实现如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // for access() function

#define MAX_STUDENTS 100
#define MAX_NAME_LEN 50
#define STUDENT_SYSTEM "student_system"
#define TRUE 1
#define FALSE 0

typedef struct {
    int id; // 学号
    char name[MAX_NAME_LEN]; // 姓名
    float score; // 成绩
} Student;

int student_count = 0; // 学生数量

Student *students; // 学生数组指针
int g_max_student = MAX_STUDENTS;

Student *stu_sys_init(int num)
{
    Student *stu_sys;
    stu_sys = malloc(num * sizeof(Student));
    if (stu_sys == NULL) {
        printf("student system malloc failed!\n");
    }

    return stu_sys;
}

int write_student_info(Student *s)
{
    FILE *fp = fopen(STUDENT_SYSTEM, "a");
    if (fp == NULL) {
        printf("fopen student_system failed!\n");
        return 1;
    }

    fprintf(fp, "%-4d %-10s %-.2f\n", s->id, s->name, s->score);

    fclose(fp);
    return 0;
}

int check_if_student_exsit(int id)
{
    int i;

    for(i = 0; i < student_count; i++) {
        if(students[i].id == id) {
            return 1;
        }
    }

    return 0;
}

void update_student_info(Student s, int need_write)
{
    Student *stu_reinit;
    int old_max_student;
    int i;

    if(student_count >= g_max_student) {
        old_max_student = g_max_student;
        g_max_student = g_max_student << 1;
        stu_reinit = stu_sys_init(g_max_student);
        if (stu_reinit == NULL) {
            printf("Database is full!\n");
            return;
        }
        memcpy(stu_reinit, students, old_max_student * sizeof(Student));
        free(students);
        students = stu_reinit;
    }

    if (!check_if_student_exsit(s.id)) {
        students[student_count++] = s;
        if (need_write) {
            printf("Student added successfully, all student: %d!\n", student_count);
            write_student_info(&s);
        }
    } else {
        printf("student has in db, do nothing!\n");
    }
}

void add_student()
{
    Student s;

    printf("Enter student ID: ");
    scanf("%d", &s.id);
    printf("Enter student name: ");
    scanf("%s", s.name);
    s.score = 0.0; // 初始成绩设置为0

    update_student_info(s, TRUE);
}

void print_title()
{
    printf("%-4s %-10s %-5s\n", "ID", "Name", "Score");
}

void display_all_students()
{
    int i;

    printf("-------- All students info --------\n");
    if (student_count == 0) {
        printf("No students!\n");
    } else {
        print_title();
        for(i = 0; i < student_count; i++) {
            printf("%-4d %-10s %-.2f\n", students[i].id, students[i].name, students[i].score);
        }
    }
    printf("--------      End       -----------\n");
}

/**
 *  * @arr: 有序数组
 *   * @l: 待查找区间左端点
 *    * @r: 待查找区间右端点
 *     * @x: 需要查找的元素
 *      */
int binary_search(Student *s, int l, int r, int target)
{
    int m;

    while (l <= r) {
        // 计算中间位置
        m = l + (r - l) / 2; // 防止(l+r)直接相加导致的溢出

        // 检查x是否存在于中间位置
        if (s[m].id == target)
            return m;

        // 若x大,则忽略左半部分
        if (s[m].id < target) {
            l = m + 1;
        } else {
            // 若x小,则忽略右半部分
            r = m - 1;
        }
    }

    // 若未找到元素,返回-1
    return -1;
}

void find_student_by_id()
{
    int id, i, ret;

    printf("Enter student ID to search: ");
    scanf("%d", &id);

    ret = binary_search(students, 0, student_count, id);
    if (ret != -1) {
        print_title();
        printf("%-4d %-10s %-.2f\n", students[ret].id, students[ret].name, students[ret].score);
        return;
    }

    printf("Student with ID %d not found!\n", id);
}

void find_student_by_name()
{
    int i, is_find = 0;
    char name[MAX_NAME_LEN];

    printf("Enter student name to search: ");
    scanf("%s", name);

    for(i = 0; i < student_count; i++) {
        if(strcmp(students[i].name, name) == 0) {
            print_title();
            printf("%-4d %-10s %-.2f\n", students[i].id, students[i].name, students[i].score);
            is_find = 1;
        }
    }

    if (is_find == 0) {
        printf("Student with name %s not found!\n", name);
    }
}

void add_score()
{
    int id, i;
    float score;

    printf("Enter student ID: ");
    scanf("%d", &id);
    printf("Enter student score: ");
    scanf("%f", &score);

    for(i = 0; i < student_count; i++) {
        if(students[i].id == id) {
            students[i].score = score;
            printf("Score added successfully!\n");
            return;
        }
    }
    printf("Student with ID %d not found!\n", id);
}

void display_average_score()
{
    float total = 0.0;
    int i;

    for(i = 0; i < student_count; i++) {
        total += students[i].score;
    }

    printf("Average score of all students: %.2f\n", total / student_count);
}

int init_student_info()
{
    if(access(STUDENT_SYSTEM, F_OK) != 0) { // 文件不存在
        return 0;
    }

    FILE *fp = fopen(STUDENT_SYSTEM, "r");
    if (fp == NULL) {
        printf("fopen student_system failed!\n");
        return 1;
    }

#define BUF_SIZE 1024
    char buf[BUF_SIZE];
    int i = 0;
    Student s;
    while(fgets(buf, BUF_SIZE - 1, fp) != NULL) {
        sscanf(buf, "%d %s %f\n", &s.id, s.name, &s.score);
        update_student_info(s, FALSE);
    }

    fclose(fp);
    return 0;

}

void swap(Student *a, Student *b)
{
    Student tmp = *a;
    *a = *b;
    *b = tmp;
}

void bubble_sort_by_score(Student *s, int n)
{
    int i, j;

    for (i = 0; i < n-1; i++) {
        for (j = 0; j < n-i-1; j++) { // 最后 i 个已经排序好了, 遍历未排序的部分
            if (s[j].score < s[j+1].score) {
                // 如果当前元素大于后面的元素,交换它们
                swap(&s[j], &s[j+1]);
            }
        }
    }
}

int main()
{
    int choice;
    int ret;

    students = stu_sys_init(MAX_STUDENTS);
    if (students == NULL) {
        printf("student system init failed, exit!\n");
        return -1;
    }

    ret = init_student_info();
    if (ret) {
        printf("init_student_info failed!\n");
        return 1;
    }
    display_all_students();

    do {
        printf("\nStudent Score Management System\n");
        printf("0. Exit\n");
        printf("1. Add Student\n");
        printf("2. Display All Students\n");
        printf("3. Find Student by ID\n");
        printf("4. Find Student by Name\n");
        printf("5. Add Score\n");
        printf("6. Display Average Score\n");
        printf("7. Display by Score sort\n");
        printf("Enter your choice: ");
        scanf("%d", &choice);

        switch(choice) {
            case 0:
                printf("Exiting...\n");
                break;
            case 1:
                add_student();
                break;
            case 2:
                display_all_students();
                break;
            case 3:
                find_student_by_id();
                break;
            case 4:
                find_student_by_name();
                break;
            case 5:
                add_score();
                break;
            case 6:
                display_average_score();
                break;
            case 7:
                bubble_sort_by_score(students, student_count);
                display_all_students();
                break;

            default:
                printf("Invalid choice!\n");
        }
    } while(choice != 0);

    return 0;
}
  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值