简介:《数据结构与算法课程设计学生成绩管理系统》是一份面向高校计算机专业课程设计任务的完整参考文档,主要帮助学生完成成绩管理系统的需求分析、系统设计、编码实现与测试维护全流程。内容围绕C++语言与Client-Server架构展开,覆盖链表、结构体等数据结构应用,以及排序、查找等算法在成绩统计与排名中的实现,同时包含数据库表设计、界面模块划分和项目进度管理等关键部分。资源包共1个doc文件,大小1.14MB,属于综合型课程设计报告,既可作为毕业论文或课程设计撰写的范本,也可用于学习如何将数据结构与算法知识落地到实际管理系统中。该资源在CSDN已有672人学习,适合正在准备数据结构与算法课程设计、需要快速梳理报告结构与代码思路的学生参考。
1. 学生成绩管理系统课程设计:比“能跑”更重要的是“能讲”
每学期期末,都会有人拿着“数据结构与算法课程设计学生成绩管理系统.doc”这个题目来问:代码在网上找了、报告也拼了,但一改就崩,答辩一问就卡壳。这个题目本身不难,本质上就是一个在内存里维护学生记录、支持增删改查、排序统计的小系统;难的是它把数据结构课里的顺序表、链表、查找、排序全串进了同一个业务,哪一块没吃透都会变成翻车现场。这篇文章从最常见的 C 语言方案出发,把系统骨架、关键代码、参数细节、答辩踩坑点完整拆一遍。适合正在做课程设计、准备期末大作业、或者想拿这个项目练手数据结构与算法的人。读完以后,你至少能把系统从“能跑”改成“能讲清楚”。
2. 先定骨架:学生成绩管理系统要用哪些数据结构与算法支撑
2.1 从需求倒推:成绩管理真实涉及的操作清单
先把课程设计的需求盘清楚。几乎所有学生成绩管理系统都逃不开这几类操作:录入学生信息、按学号查询、修改某条记录、删除学生、按总评成绩排名、统计平均分和及格率、把数据保存到文件、下次启动时重新载入。
把这几个操作翻译成数据结构语言,就是:
- 录入:往容器尾部追加一个元素,同时要检查学号是否重复;
- 查询:在一个无序集合里按学号做等值匹配;
- 删除:找到指定位置后把后继元素整体前移;
- 排序:按总评成绩做降序排列,且成绩相同时最好保持录入顺序;
- 统计:遍历全部元素,做累加和比较;
- 文件读写:把内存里的连续区域按固定格式序列化到磁盘。
这个映射关系看起来很朴素,但作用很大。很多人的课设报告写不好、答辩被问住,就是因为跳过了这一步,一上来就写代码,最后连“为什么用数组而不用链表”都答不上来。
实际操作时,我一般会把上表再细化一层,写成需求与数据结构对照表,直接放进报告的功能设计一节。例如:录入对应“顺序表尾插”,查询对应“顺序查找+折半查找”,删除对应“顺序表删除指定位置”,排序对应“冒泡排序/直接插入排序/快速排序”。
2.2 顺序表还是链表:选型理由和真实课程设计要求
学生成绩管理系统到底用顺序表还是链表,是答辩时最常被问到的问题,也是选型上最值得说清楚的点。
顺序表(结构体数组)的优点是:内存连续,支持下标随机访问,排序时交换元素、折半查找时取中间位置都极其顺手;缺点是中间插入删除要移动大量元素,扩容要 realloc。链表恰好反过来:插入删除只需修改指针,但排序麻烦,折半查找更是基本不可用,因为链表不是随机存取结构。
对“学生成绩管理系统”这个题目而言,数据量就是一到两个班,100 条左右,算上平时、期中、期末、总评四个分数,内存开销完全可忽略。这时候顺序表就是最高性价比的选择。真实工程里数据量大、增删频繁时顺序表的移动成本会凸显,但课程设计不追求这个,课程设计的核心是让你把顺序表和链表各自的优缺点在报告里说清楚。
如果老师明确要求“必须用链表”,那就把主体结构改成单链表。注意这时查找只能顺序查找,排序最好使用插入排序或链表化的归并思路,不要硬套数组版的快速排序,否则光是交换指针就能让人改到心态爆炸。常见做法是:主体用顺序表交一版,再额外写一个小 demo 展示链表头插、尾插、删除,报告里对比一下两种结构,既满足了题目要求,也显得有余力。
2.3 排序与查找算法的选型边界
算法选择也要围绕业务数据特征来说。100 条成绩记录,排序列是总评成绩,查找列是学号。这里最合理的组合是:
- 排序:冒泡排序或直接插入排序,选一个稳定排序,再加一个快速排序作对比;
- 查找:学号精确匹配用顺序查找;若把学号按升序排好,则可以用折半查找,这是报告里最明显的加分点;
- 统计:一趟遍历完成平均分、及格率、最高分、最低分。
不要一上来就堆 KMP、堆红黑树,这个系统里没有长串匹配的强需求,也没有动态平衡的必要。如果报告想体现算法知识面,可以提“姓名模糊查找用朴素匹配就够了,KMP 在这个数据规模下反而看不出优势”,一句话带过即可,硬写反而会被评委追问细节。
这里要特别说一个“稳定排序”的概念。总评成绩相同的情况很常见,比如 85 分可能有好几个人。如果按总评降序排序,且成绩相同时希望学号小的排在前面,用稳定排序最简单——先按学号升序排好,再按总评做稳定降序排序,成绩相同者的相对次序就不会乱。冒泡和直接插入在“只交换严格大于/严格小于”的条件下是稳定的,快速排序不稳定,这一点报告里必须写准确。
3. 用结构体数组把系统跑起来:核心定义、录入、查询、删除
3.1 学生结构体与顺序表定义:先写对数据类型再谈算法
课程设计的第一课是定义数据对象。我见过不少同学一上来就写 int score[100],结果后面想加一个姓名都得全局改。正确的做法是先定义学生类型,再定义顺序表类型。
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_STUDENT 120 #define NAME_LEN 24 #define ID_LEN 12 typedef struct { char id[ID_LEN]; // 学号,字符串存储,保留前导0 char name[NAME_LEN]; // 姓名,不含空格 float daily; // 平时成绩 0~100 float midterm; // 期中成绩 float final; // 期末成绩 float total; // 总评成绩 } Student; typedef struct { Student data[MAX_STUDENT]; int len; // 当前学生数量 } ScoreList;学号必须用字符串而不是 int 存储。很多学号是 2024010101 这种格式,如果当成 int,前导零会丢,第二次从文件读回来就匹配不上了。总评成绩用 float,因为平时、期中、期末加权平均后大概率带小数,用 int 会造成排名同分过多。
MAX_STUDENT 设成 120 是够用的。如果担心人数超出,可以改成动态扩容方案:ScoreList 里换成 Student *data,配合 int capacity,满时用 realloc 按 1.5 倍或 2 倍扩容。这一步写进报告是加分项,但代码量会增加不少,自己权衡。
3.2 录入与删除:scanf 的坑和健壮性写法
录入函数是系统最基础的部分,也是边界检查最容易漏的地方。下面这个版本检查了容量上限和学号重复,两个检查缺一个都会在答辩时被当场指出问题。
void addStudent(ScoreList *list) { if (list->len >= MAX_STUDENT) { printf("学生数量已满,无法添加\n"); return; } Student s; printf("请输入学号: "); scanf("%s", s.id); for (int i = 0; i < list->len; i++) { if (strcmp(list->data[i].id, s.id) == 0) { printf("学号 %s 已存在,录入失败\n", s.id); return; } } printf("请输入姓名: "); scanf("%s", s.name); printf("请输入平时 期中 期末成绩(用空格分隔): "); scanf("%f%f%f", &s.daily, &s.midterm, &s.final); s.total = 0.3f * s.daily + 0.3f * s.midterm + 0.4f * s.final; list->data[list->len++] = s; printf("录入成功,总评成绩为 %.1f\n", s.total); }这里有几个细节。成绩加权公式我用了 0.3、0.3、0.4,这是最常见的比例,具体看课程要求,改成 0.2、0.2、0.6 也只在计算 total 时动一行。录入时没有做成绩范围检查(0~100),这是故意留的简单校验点:在三个成绩读入后各加一个 if 判断,超过范围就 return,报告里写“输入合法性校验”一节时能补上。
scanf("%s", s.id) 在读字符串时遇到空格会停,所以姓名不能包含空格。如果系统支持英文全名,比如“Zhang San”,scanf 就废了,换成 fgets 再手动去掉末尾换行。课程设计里中文姓名没有空格,scanf 够用,还能避开 fgets 和 scanf 混用的脏缓冲区问题,这个坑后面避坑章节会细说。
删除操作的基本思路是先定位再整体前移,这正好演示了顺序表“删除需要移动元素”的特点:
void deleteById(ScoreList *list, const char *id) { int pos = -1; for (int i = 0; i < list->len; i++) { if (strcmp(list->data[i].id, id) == 0) { pos = i; break; } } if (pos == -1) { printf("未找到学号 %s\n", id); return; } for (int i = pos; i < list->len - 1; i++) { list->data[i] = list->data[i + 1]; // 结构体整体赋值,等价于逐字段复制 } list->len--; printf("删除成功\n"); }删除的时间复杂度是 O(n),因为要先顺序查找,找到后还要移动平均 n/2 个元素。报告里写复杂度时,不要只写“O(n)”,最好拆成“查找 O(n) + 移动 O(n)”,显得分析细腻。
3.3 查找与统计:顺序查找学号,折半查找前先排序
按学号精确查找在数据量小时直接用顺序查找,代码最直白:
void findByID(ScoreList *list, const char *id) { for (int i = 0; i < list->len; i++) { if (strcmp(list->data[i].id, id) == 0) { printf("学号: %s 姓名: %s 平时: %.1f 期中: %.1f 期末: %.1f 总评: %.1f\n", list->data[i].id, list->data[i].name, list->data[i].daily, list->data[i].midterm, list->data[i].final, list->data[i].total); return; } } printf("未找到该学生\n"); }如果想上折半查找,前提是数组已经按学号升序排序。这里容易犯一个逻辑错误:前面按总评成绩排序后,数组的学号顺序被打乱了,直接折半查找会出错。所以折半查找应该单独维护一个“按学号有序”的访问视图,或者在一开始时就把原始录入顺序保存一份。我一般会先复制一份学号索引数组,对索引排序,再用索引配合原数组做折半查找,这样不会破坏成绩排序结果。
统计模块最常见的需求是平均分、及格率、分数段分布。遍历一次全部解决:
void statistics(ScoreList *list) { if (list->len == 0) { printf("系统内暂无学生数据\n"); return; } float sum = 0; int passCnt = 0; int scoreDist[5] = {0}; // 优 良 中 及格 不及格 for (int i = 0; i < list->len; i++) { float t = list->data[i].total; sum += t; if (t >= 60) passCnt++; if (t >= 90) scoreDist[0]++; else if (t >= 80) scoreDist[1]++; else if (t >= 70) scoreDist[2]++; else if (t >= 60) scoreDist[3]++; else scoreDist[4]++; } printf("平均总评: %.1f\n", sum / list->len); printf("及格率: %.1f%%\n", passCnt * 100.0 / list->len); printf("优秀(90+): %d 良好(80-89): %d 中等(70-79): %d 及格(60-69): %d 不及格: %d\n", scoreDist[0], scoreDist[1], scoreDist[2], scoreDist[3], scoreDist[4]); }注意及格率计算时 passCnt 是 int,乘 100.0f 而不是 100,否则整数除法会把结果变成 0。这是 C 语言课程设计里最经典的隐形小错误。
3.4 文件读写:文本格式持久化与 fscanf 的对应关系
文件保存建议用文本格式而不是二进制格式。文本格式可以直接用记事本打开检查,答辩演示时也更直观。核心就两个函数。
void saveToFile(ScoreList *list, const char *path) { FILE *fp = fopen(path, "w"); if (fp == NULL) { printf("无法打开文件 %s\n", path); return; } fprintf(fp, "%d\n", list->len); for (int i = 0; i < list->len; i++) { fprintf(fp, "%s %s %.1f %.1f %.1f %.1f\n", list->data[i].id, list->data[i].name, list->data[i].daily, list->data[i].midterm, list->data[i].final, list->data[i].total); } fclose(fp); printf("数据已保存至 %s\n", path); } void loadFromFile(ScoreList *list, const char *path) { FILE *fp = fopen(path, "r"); if (fp == NULL) { printf("找不到文件 %s,本次启动使用空数据\n", path); return; } int n = 0; fscanf(fp, "%d", &n); for (int i = 0; i < n; i++) { Student *s = &list->data[i]; fscanf(fp, "%s %s %f %f %f %f", s->id, s->name, &s->daily, &s->midterm, &s->final, &s->total); } list->len = n; fclose(fp); printf("成功读取 %d 条学生数据\n", n); }第一行先写入 len,读的时候用这个数字控制循环次数,避免读到文件末尾的脏数据。每个字段用空格分隔,学号、姓名、四个成绩之间一一对应。fscanf 的格式控制字符串必须和 fprintf 完全一致,少一个 %f 整个文件都会解析错位。
写文件操作失败时之所以要先检查 fopen 返回值,是因为磁盘满、目录没有权限、路径不存在都是课程设计里容易出现的真实问题。有的同学在答辩现场把数据文件放在只读目录里,保存提示失败,当场就慌了。提前做好这个判断,至少能给出一个明确的提示信息。
4. 排序算法模块:冒泡、直接插入、快速排序的实现与性能对比
4.1 冒泡排序:用成绩降序排名的同时保住学号信息
冒泡排序是课程设计的“保底算法”。绝大多数学生的第一版代码就是冒泡,关键问题是排序时只交换了 total 一个字段,没有把整个 Student 结构体一起交换,导致排完序后成绩排名对不上姓名。
// 返回比较次数 + 移动次数,供报告性能分析使用 int bubbleSort(Student *a, int n) { int cmp = 0, mov = 0; for (int i = 0; i < n - 1; i++) { int swapped = 0; for (int j = 0; j < n - 1 - i; j++) { cmp++; if (a[j].total < a[j + 1].total) { // 严格小于才交换,保证稳定 Student tmp = a[j]; a[j] = a[j + 1]; a[j + 1] = tmp; mov += 3; // 三次赋值:tmp、a[j]、a[j+1] swapped = 1; } } if (!swapped) { break; // 本轮无交换,说明已有序,提前结束 } } return cmp + mov; }比较用<而不是<=,这行非常关键。如果写成<=,两个总评成绩相同的人也会交换顺序,稳定排序就变成了不稳定排序。报告里写“冒泡排序是稳定的”这句话时,要在代码里用严格小于来表示“只有严格逆序才交换”。提前退出的 swapped 标记可以在数据基本有序时把最好情况变成 O(n),这是冒泡排序不多的亮点。交换 3 次赋值,这个细节放在复杂度分析里可以体现你真的理解了“移动次数”的含义。
4.2 直接插入排序:最适合“成绩表基本有序”场景
插入排序在数据基本有序时性能很好,这个特性正好适合处理“学生成绩已经有学号顺序,再按总评微调”的场景。它的思想是把当前元素往前插入到已排序区间的正确位置。
int insertSort(Student *a, int n) { int cmp = 0, mov = 0; for (int i = 1; i < n; i++) { Student tmp = a[i]; int j = i - 1; while (j >= 0 && a[j].total < tmp.total) { cmp++; a[j + 1] = a[j]; // 大元素后移 mov++; j--; } cmp++; // 最后一次条件失败也要算一次比较 a[j + 1] = tmp; mov++; } return cmp + mov; }插入排序的比较次数统计不好精确写,因为 while 循环里“条件失败”的最后一次比较也占一次比较次数。很多同学报告的统计表数字对不上理论值,原因就在这。我自己的习惯是:代码里的 cmp 只统计进入循环体的成功比较,最后一次失败比较单独加一次;移入 tmp 也算一次移动。这样统计出来的数字和教材的计量口径基本一致。
边界 case:当 a[j].total 全部比 tmp 小时,while 会一直执行到 j == -1,代码里j >= 0的条件保证不会越界访问 a[-1]。这是数组类排序里最容易出现越界的角落,编译器不一定报错,但运行结果可能随机出错。
4.3 快速排序:两个指针一个轴值,注意递归边界
快速排序是报告里最能体现算法水平的排序,也是答辩老师最爱深挖的一个。下面这个版本以首个元素为轴值,左右交替挖坑填数:
void quickSort(Student *a, int low, int high) { if (low >= high) { return; // 递归出口:区间为空或只有一个元素 } Student pivot = a[low]; int i = low, j = high; while (i < j) { while (i < j && a[j].total <= pivot.total) j--; a[i] = a[j]; while (i < j && a[i].total >= pivot.total) i++; a[j] = a[i]; } a[i] = pivot; quickSort(a, low, i - 1); quickSort(a, i + 1, high); }这里要特别提醒:方向不能写反。因为是降序排序,当右指针 j 从右向左找的是“小于等于轴值”的元素,左指针 i 从左向右找的是“大于等于轴值”的元素。两个 while 里都用<=和>=,遇到与轴值相等的元素时指针继续移动,能让轴值两边相对均衡一些,也会让快排变得不稳定。报告里要明确写“快速排序是不稳定的”,如果要保持成绩相同者按学号有序,就得额外加学号作为第二关键字比较。
轴值选取只取 a[low],在最坏情况下(已经有序的数组)递归深度会变成 O(n),导致栈溢出,这是快排的经典翻车点。如果想让代码更稳,可以用“三数取中”:把 low、mid、high 三个位置的中位数作为轴值,再与 a[low] 交换。这段改进代码不难写,加进去以后在答辩时可以顺势讲清楚“快排的最坏情况和优化手段”。
4.4 三种排序参数对比:报告里要写的表
课程设计报告里的排序对照表,重点不是抄教材的复杂度结论,而是给出自己代码运行出的实测数据。录入 100 条成绩后分别调用三个排序函数,打印出 cmp + mov 的返回值,填到类似下面的表中:
| 排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 | 100条数据实测比较+移动次数 |
|---|---|---|---|---|---|
| 冒泡排序 | O(n²) | O(n²) | O(1) | 稳定 | 约 8000~12000 |
| 直接插入排序 | O(n²) | O(n²) | O(1) | 稳定 | 约 6000~11000 |
| 快速排序 | O(n log n) | O(n²) | O(log n) | 不稳定 | 约 1000~2000 |
实测次数会随数据分布变化很大,不要照着表里的约数硬编。真实做法是排序函数把 cmp 和 mov 加起来返回,菜单里调用后直接 printf 出来。这份实测数据放到报告里,比任何教材截图都有说服力。
报告的对比分析段落可以这样写:冒泡和插入在数据规模小、基本有序时差距不大;快速排序在随机数据上优势明显,但最坏情况退化到 O(n²) 且不稳定;如果要求稳定排序且数据量较大,应选择归并排序——这是课程设计里顺势引出“归并排序算法”的好位置。
5. 课程设计避坑:5 个让答辩现场翻车的细节
5.1 现象:排序跑完,名字对不上成绩
这是最经典的翻车现场。代码里只写了for (i=0; i<n-1; i++) if (score[i] < score[i+1]) { t = score[i]; score[i] = score[i+1]; score[i+1] = t; },排完序后总评成绩是降序了,但对应的姓名、学号完全错位。原因是只交换了 score 这一个字段,没有整条记录整体交换。
解决方法是每次交换都交换整个 Student 结构体,即Student tmp = a[j]; a[j] = a[j+1]; a[j+1] = tmp;。结构体整体赋值在 C 语言里是合法的,编译器会按字节复制。不要图省事只换成绩字段,这是排序算法模块最容易埋雷的地方。
5.2 现象:从文件读入的学生姓名出现乱码或末尾多一个空行
用文本格式保存数据时,中文姓名经常在重新打开时变成乱码。原因是控制台默认编码不一致:Windows 下编译运行默认是 GBK,Linux 下默认是 UTF-8。同一份数据文件在两个环境里互读,中文必然乱码。这是编码问题,不是程序逻辑问题。
解决方法是课程设计统一到一个环境里开发和演示。如果坚持要跨平台,就保存时把姓名转成 Unicode 编码存储,但这样复杂度超标,不推荐在课设里做。另一个更隐蔽的问题是:用 fprintf 写入时行尾是\n,Windows 下会变成\r\n,再用 fscanf 读回时%s会自动跳过空白符,所以姓名的末尾一般不会带\r;但如果用了 fgets 读取,姓名字符串尾部就会挂一个\r,导致按学号匹配时永远找不到人。解决方法是读取后手动检查字符串末尾,如果是\r或\n就置为\0。
5.3 现象:链表插入后程序死循环或输出少了一条数据
菜单要求用链表实现时,出现死循环大多发生在尾插和头插的指针操作上。常见错误是:尾插时只修改了新结点的 next,没把原尾结点的 next 指向新结点;头插时又先让 head 等于新结点,再设置新结点的 next,结果把链表从中间切断了。
解决方法是画图再写代码。头插的正确顺序是newNode->next = head; head = newNode;,先让新结点接住旧链表的头,再更新 head。尾插的正确顺序是遍历到p->next == NULL,然后p->next = newNode; newNode->next = NULL;。链表的指针题没有玄学,每一条链都要保证双向连接完整。实在不行,把链表结点个数打印出来,插入一条打一次 len,能快速定位断链位置。
5.4 现象:菜单输入数字后,下一次 getchar 直接读到换行
菜单循环里经常混用 scanf 和 getchar。用户输入1后按回车,缓冲区里存的是'1' 和 '\n'。scanf("%d") 把'1'读走,'\n'留在了缓冲区。下一次菜单用 getchar 等待按键时,直接读到换行符,程序像抽风一样连续跳菜单。
解决方法是明确使用场景:菜单选择用 scanf("%d"),输入后立即加一行while (getchar() != '\n');清空缓冲区;需要输入单个字符时用scanf(" %c", &ch),在%c前面加一个空格跳过空白符。这两种写法学一个用即可,不要混着来。
5.5 现象:答辩被问“为什么不用数据库”
“学生成绩管理系统”听上去很像数据库课设,所以评委大概率会问:为什么用结构体数组不用 MySQL?这个问题回答得好是加分项,回答不好容易露怯。
原因其实很清晰:这门课程叫“数据结构与算法”,考核目标是顺序表、链表、排序、查找这些内存数据结构。顺序表就是数据库表在内存中的一种简单映射,文件保存就是最基础的持久化。真实系统当然会用数据库,但课程设计的范围限定在“不使用数据库的前提下,用数据结构组织内存数据”。报告里写一段“数据库方案对比”,说明数据库适合多用户并发、数据量大、需要事务支持的场景,但会把课设重点从算法转移到 SQL,偏离课程目标。这样回答既承认数据库的合理性,又扣住了课程设计的主旨。
6. 验证与演示:让课程设计从“能跑”到“能讲清楚”
6.1 测试用例清单:照着走一遍再演示
不要到答辩现场才第一次从头运行。我每次做这类课设都会准备一张测试用例表,按顺序执行:
| 测试项 | 操作 | 预期结果 |
|---|---|---|
| 空系统启动 | 不加载文件直接进入菜单 | 查询/统计提示无数据,不崩溃 |
| 录入第一条 | 输入学号 20240001 | 提示录入成功,len 变为 1 |
| 重复学号 | 再次输入 20240001 | 提示学号重复,拒绝录入 |
| 删除首尾中间 | 分别删除位置 0、末尾、中间 | 输出正确剩余数量 |
| 按学号查找 | 输入存在/不存在的学号 | 正确匹配或提示未找到 |
| 成绩相同排序 | 录入两个总评相同者 | 稳定排序保持录入顺序 |
| 非法成绩 | 录入成绩 150 | 应被校验拦截(或说明未做校验) |
| 保存重读 | 保存后重启程序再载入 | 数据完全一致 |
演示时按这个顺序走,评委看到的是有逻辑的完整验收,而不是东点一下西点一下。
6.2 答辩演示路径:5 分钟讲完的顺序
我的演示路线基本固定:先讲需求分析,展示文件里的成绩数据长什么样;再讲结构体设计和顺序表选择理由,点开代码指出 len 字段和数组上限;接着现场录一条数据,演示学号重复检查;然后删一条、查一个学号;最后按总评排序,打印排序统计次数,结合报告里的对照表解释为什么快排次数明显少。全部动作不拖动鼠标乱翻代码,讲到哪一步对应的数据结构和算法就切到哪个函数。保持节奏比展示功能更让评委舒服。
还能再加一个便宜的进阶点:写一个 hash 查找学号的小函数,用学号后四位做散列位置,冲突时链地址法处理。这个函数总共不到 50 行,答辩时讲“为什么哈希查找在 100 条数据里看不出优势,但数据量上万时明显更快”,就能把课设从数据结构展示升到算法设计层面。归并排序同理,写代码太重的话,把思路和归并过程画进报告的扩展章节即可。
我做这类课设的习惯是:先把主流程跑通,再补边界检查,最后才写报告。报告不是代码的复述,而是把“为什么这么设计”讲清楚。代码能跑只是及格,能讲出选型理由和边界坑才拿得到高分。希望帮到你。
本文还有配套的精品资源,点击获取