
简介南京航空航天大学数据结构课程设计代码加报告整理自2019—2020年秋季学期的个人原创作品适合正在学习数据结构、需要完成课程设计或想参考完整实现思路的本科学生。压缩包共76个文件约6.2MB以36个cpp源代码文件为主体配合31个txt数据/输出文件、6个exe可执行程序及1份docx课程设计报告覆盖排序基数排序、希尔排序、归并排序、Kruskal最小生成树、哈夫曼树、邻接表图结构等多个经典主题同时也包含若干题目的完整实现版本与输入数据便于串联理论学习、编码实现与运行验证。报告部分对设计思路、数据结构选型和关键实现做了说明txt文件可用于不同数据规模的测试exe则方便直接查看运行效果降低调试门槛。目前已有2789人浏览学习作为一份价格不高的原创资料能帮助读者节省理解课程设计的时间并在代码层面提供具体指导尤其适合想借完整案例巩固算法与数据结构的在校学生。1. 课程设计不是“写代码”是“证明你会写代码”某航空航天类高校的数据结构课程设计最终交付物是两样一个能跑的代码工程一份从需求到测试的完整报告。很多同学把精力全砸在“把代码跑通”上觉得报告是抄抄模板的体力活结果答辩时被老师一句“你这段删除操作如果有多个相同节点怎么办”问住。反直觉的结论是课程设计真正的评分点是你怎么组织代码和怎么写报告——代码展示的是数据结构有没有用对报告展示的是你有没有想清楚为什么这样用。这篇文章把选题、代码组织、报告结构和答辩前排查串成一条可照抄的路径适合正在准备课程设计、想拿一档实在分数、而不是靠复制粘贴蒙混过关的低年级同学。2. 选题与数据结构选型把评分点拆进题目里2.1 三类常见选题的“出分”逻辑课程设计最常见的有三类方向先不急着决定做哪个先看每类在老师那儿的评分逻辑。一类是管理系统类比如图书管理系统、学生信息管理。核心要求是顺序表或者链表存储、增删改查、文件读写、排序查找。这类题目的好处是需求直观现场演示时老师一眼能看懂界面在干什么风险也很明显如果只写“能增删改查”数据结构含量偏低打分很容易落在中档。要想往上走必须主动往题目里加知识点比如按书名的模糊查找用顺序遍历按 ISBN 的精确查找用哈希索引借阅排行用排序这样“存储、查找、排序、索引”就全有了。一类是算法演示类比如排序算法比较、迷宫求解、表达式求值。核心是栈、队列、递归和排序算法这类题目算法特性强复杂度分析容易讲清楚适合展示“算法设计”的深度但缺点是不像管理系统那么有“产品感”演示起来比较干需要把输出设计得层次分明比如排序过程逐步打印、迷宫路径用坐标序列打印。还有一类是综合应用类比如哈夫曼编码压缩、校园导航最短路径、家谱树构建。核心是树、图、优先队列和贪心策略数据结构覆盖面最广报告最好写厚但代码量也是最大的容易出现指针、递归和内存问题对调试能力要求更高。我一般建议的做法是选“管理系统为主、算法演示为辅”的组合比如图书管理系统里增加一个表达式求值或排序比较模块。这样演示时既直观评分的知识点覆盖也全面。下表是一个大致的对照选题方向核心结构主要评分点最大风险管理系统类顺序表/链表、哈希、排序增删改查的完整性、文件持久化结构单一分档上不去算法演示类栈、队列、递归算法正确性、复杂度分析演示枯燥界面太小综合应用类树、图、优先队列结构覆盖面、设计能力代码量大错误难查2.2 数据结构与算法的对应选型能用一句话说出“为什么”选好题目方向后下一步是逐个功能确定用什么数据结构。我习惯列一张“需求-结构-算法”对照表把每个功能模块都钉死后面写代码和报告都不会乱。先从存储主线说起。图书、学生这类数据最常见的选择是链表和顺序表。链表插入删除不需要移动元素但随机访问要遍历顺序表按下标访问快但中间插入要搬动数据。课程设计数据量都不大这一点性能差别其实感觉不出来老师真正想听的是你能不能说清楚“为什么这里用链表”。一个能直接用的回答思路是本系统的核心操作是频繁添加和删除图书且图书数量动态变化没有固定上限所以选择单链表如果核心操作是按下标或按编号频繁随机访问那就应该选顺序表。查找功能要再往上走一层。按 ISBN 精确查找时线性遍历是 O(n)如果题目要求“查找响应快”可以给图书表加一层哈希索引哈希表的平均查找是 O(1)代价是额外空间。按书名查找通常做模糊匹配哈希反而不合适保留顺序遍历。这样一套组合下来同一个系统里既有链表又有哈希表报告里的“概要设计”也有东西可写。树和图用在算法模块里。表达式求值用栈把中缀转后缀哈夫曼编码用优先队列每次取两个最小权值节点建树校园导航用带权图的 Dijkstra 求单源最短路径迷宫求解用栈做深度优先或队列做广度优先。每选一个结构都要准备一句话解释“为什么不用另一个”。比如 Dijkstra 为什么不用 Floyd因为单源最短路径只需求一次源点到所有点Dijkstra 是 O((VE)logV)Floyd 是 O(V³)在点少但边多的图上也可以选 Floyd关键是报告里要把这个选择的依据写出来。功能需求选取的数据结构复杂度说明图书信息存储单链表频繁增删避免搬移数据ISBN 精确查找哈希表附带平均 O(1) vs 链表 O(n)书名模糊查找链表顺序遍历模糊匹配天然需要逐个比对中缀表达式计算栈双栈或后缀运算符优先级依赖栈借阅统计排序快速排序顺序表辅助排序需求稳定且适合分治提示这部分不需要追求“最优”但要能自圆其说。评分不是比算法多先进而是比“选型有依据”。一个 O(n²) 的简单排序只要你在报告里写清楚在什么数据量下够用就比盲目贴一个说不清的复杂算法强。下面是用这套选型思路做图书管理系统时的核心结构定义先看代码// book.h typedef struct Book { long long isbn; // 用 long long 存 ISBN避免 int 溢出 char name[64]; // 书名限长方便控制台排版 char author[32]; // 作者 int stock; // 库存数量 struct Book *next; // 链表后继指针 } Book;这段定义说明了存储层的骨架。ISBN 用 long long 是常见做法因为标准 ISBN 去掉横线后有 13 位int 只有 32 位放不下书名和作者用定长数组是为了简化控制台排版和文件解析定长意味着读写时不需要动态分配内存答辩时少一个“内存泄漏”的提问点。next 指针把节点串成单链表所有增删改查都围绕它展开。课程设计的存储结构不必一开始就用复杂方案先把链表的插入、删除、遍历写扎实再考虑哈希索引。3. 把代码写成“能被答辩提问”的样子持久化、接口与测试用例3.1 最小可演示系统从控制台到文件持久化课程设计要求“可运行”意味着程序关闭后再打开数据还在。我见过不少同学把数据只放在内存里一重启就清空演示时被老师一句“你的数据存在哪里”问住。所以文件持久化不是加分项是必备项。最小可演示系统的做法是程序启动时从指定文件读入数据构建链表退出时把链表写回同名文件。下面是一个最小骨架上最关键的存盘和读盘函数C 写法// file_io.cpp #include cstdio #include cstdlib bool saveBooks(Book *head, const char *path) { FILE *fp fopen(path, w); if (!fp) return false; // 文件打开失败要向上层报告 for (Book *p head-next; p ! NULL; p p-next) { fprintf(fp, %lld %s %s %d\n, p-isbn, p-name, p-author, p-stock); } fclose(fp); return true; } Book *loadBooks(const char *path) { Book *head (Book *)calloc(1, sizeof(Book)); // 带头节点的空链表 FILE *fp fopen(path, r); if (!fp) return head; // 文件不存在就返回空表不要崩溃 Book *tail head; while (true) { Book *node (Book *)calloc(1, sizeof(Book)); if (fscanf(fp, %lld %s %s %d, node-isbn, node-name, node-author, node-stock) ! 4) { free(node); // 读到结尾或半行数据释放后退出 break; } tail-next node; tail node; } fclose(fp); return head; }逻辑说明saveBooks 遍历链表每一条记录写成一行字段之间用空格分隔loadBooks 先构造一个头节点再逐行读取并尾插法挂到链表尾部。两个函数都把文件路径作为参数传入而不是写死成全局路径这样测试时可以直接换一个临时文件不必反复污染正式数据。参数说明path 传入相对路径“books.txt”即可程序的工作目录决定了它实际落在哪里fscanf 的格式串要和 fprintf 完全对应注意 long long 用 %lld否则读出来是错的读文件时 fscanf 返回值不等于 4 就意味着一行数据缺失或已经到文件尾此时释放节点并跳出循环避免把不完整的记录挂进链表。如果不想用 C 的 FILE 读写也可以用 C 的 ifstream/ofstream但 FILE 在这类小项目里有三个实际好处格式化控制精确输出到控制台和文件可以共用同一套 fprintf 逻辑错误处理直观课程设计的报告里解释起来很短。关键不是语言而是“文件不存在”“文件被占用”这类异常情况至少要有一个返回值给上层处理而不是让程序直接崩溃。3.2 接口与参数约定把“可调用”写清楚老师的追问通常在函数层面你这个函数参数为什么这样设计如果代码里全是几十行的 main 函数连函数签名都没有这个问题就没法回答。我一般会把系统拆成下面几个函数每个函数只做一件事函数签名功能关键参数说明Book* createBook()控制台输入一本图书内部完成输入校验void insertBook(Book* head, Book* node)按 ISBN 有序插入头节点指针 新节点指针Book* findBook(Book* head, long long isbn)按 ISBN 精确查找返回节点指针找不到返回 NULLbool removeBook(Book* head, long long isbn)按 ISBN 删除返回是否删除成功void printBooks(const Book* head)表格化输出全部图书只读遍历不修改链表void sortByStock(Book* head)按库存排序用选择排序或快排这些函数的设计原则是通过参数传入链表头而不是用全局变量输入输出分离控制台打印放在调用方数据操作放在函数里。这样答辩时被问到“怎么测试单个函数”可以直接说“写一个临时 main 调用它”。删除函数是链表题里必被问的细节因为它要处理三种位置头节点后、中间、尾节点。核心实现如下bool removeBook(Book *head, long long isbn) { for (Book *prev head, *cur head-next; cur ! NULL; prev cur, cur cur-next) { if (cur-isbn isbn) { prev-next cur-next; // 跳过当前节点 free(cur); // 释放被删节点的内存 return true; } } return false; // 没找到返回 false }逻辑说明用 prev 和 cur 两个指针一前一后遍历找到目标后让前一个节点直接指向目标的后继然后释放目标节点。这个写法同时覆盖了删除第一个真实节点和删除中间节点的情况因为遍历始终从 head 开始prev 最初指向头节点。参数说明head 是链表的头指针即使链表只剩下一个真实节点删除后 head-next 变成 NULL链表依然合法isbn 用 long long 是因为前面结构体里已经定了类型保持类型一致可以避免隐式转换带来的比较错误。返回值设计成 bool调用方可以根据返回值提示“删除成功”或“未找到该图书”这比删除失败时直接打印一段话来得更干净。3.3 测试用例与输出设计让演示不翻车代码写完以后演示顺序和测试用例同样重要。我习惯准备五组边界数据提前跑一遍并把结果记录成“测试记录表”这个表后面直接搬进报告空文件启动books.txt 不存在或为空程序能正常启动并显示空库单条记录插入第一本图书后退出再进入数据不丢失删除头节点删除列表第一本验证 head 正确更新重复 ISBN插入相同 ISBN 时拒绝或提示不能出现两条相同记录库存为 0输出时能正常显示 0不能因为除零或空指针崩溃。演示时的输出要把数据排列成表格不要一行一个字段地纵向打印。纵向打印在控制台里看不出“增删改查成功”表格输出则能让老师一眼看到变化。printBooks 里用 printf 控制列宽即可void printBooks(const Book *head) { printf(%-15s %-20s %-15s %-6s\n, ISBN, 书名, 作者, 库存); for (const Book *p head-next; p ! NULL; p p-next) { printf(%-15lld %-20s %-15s %-6d\n, p-isbn, p-name, p-author, p-stock); } }这里的参数说明重点是列宽%-15lld 表示左对齐、宽度 15书名太长时会被截断所以结构体里 name 定长 64输出列宽 20 是够用的。演示时先打印一次原始数据再做插入和删除每次操作后重新打印全场的视觉逻辑就是“操作前 - 操作后”的对比。这套输出设计并不复杂但它决定了老师能不能在三分钟里确认你的程序确实在工作。4. 报告写出“工作量”七段式结构、图表与数据支撑4.1 七段式结构与各章字数分配报告的作用不是复述代码而是把设计过程讲清楚。课程设计的报告通常按下面七个部分组织我建议按比例分配篇幅避免把“需求分析”写成两页把“详细设计”写成半页报告章节核心内容建议占比封面与摘要题目、用到的核心数据结构、完成的功能5%需求分析系统有哪些功能、面向谁、边界条件15%概要设计模块划分、数据结构选型、模块关系20%详细设计核心函数的流程、关键代码片段与说明30%测试与分析测试用例、运行截图、复杂度分析20%心得与不足遇到的问题、怎么解决的、还能怎么改进10%参考文献教材与 C/C 参考书不计入正文这个结构的核心思路是“让老师快速定位工作量”。详细设计占比最大每个核心函数都要有“算法思路 - 代码片段 - 复杂度说明”三段测试与分析要把 3.3 里的测试记录表放进去再补运行截图。心得与不足不要写“我学会了团队合作”这种空话写具体的技术教训比如“链表删除时忘记更新头节点导致崩溃通过画指针示意图定位”就很有说服力。4.2 用图表和数据把报告从“流水账”拉回“设计文档”很多同学的报告被批“像流水账”是因为全文只有文字和代码没有一张图、一张表。我一般会画三样东西系统功能结构图、核心操作流程图、测试数据表。功能结构图用工具画成树状结构根节点是系统名子节点是“图书管理”“查询统计”“文件操作”三个模块再往下是“添加/删除/修改/查找”。这张图放在概要设计第一页老师扫一眼就明白系统的全貌。核心操作流程图只需要画删除和查找两个用方框和箭头表示判断与走向不需要用正规的流程图符号画到滴水不漏。测试数据表则直接把 3.3 的测试记录整理成三列测试场景、预期结果、实际结果。这三样放进去报告立刻从“代码粘贴本”变成“设计文档”。图表要统一编号和命名比如“图 4-1 系统功能结构图”“表 4-2 删除功能测试记录”。正文里引用图表时写“如图 4-1 所示”而不是“如下图”这样显得规范答辩时老师如果直接翻图也能找到位置。4.3 代码与报告的对应关系版本、注释和截图必须对齐报告里贴的代码、附的截图、提交的源文件三者的版本必须一致。我的做法是提交前最后一次整理时按固定的目录结构重新组织一遍course_design/ ├── main.cpp # 主程序菜单循环 ├── book.h # 结构体与函数声明 ├── book.cpp # 链表与文件操作实现 ├── books.txt # 初始测试数据 └── report/ ├── 设计报告.md # 报告源文件 └── figures/ # 所有截图与图表的源文件目录结构本身就是报告的一部分。main.cpp 只负责菜单和调用book.cpp 里是具体实现头文件里放声明这样答辩时说“功能按模块拆分”就有实物证据。注释规范也很重要每个函数头部写一行功能说明和参数含义关键步骤写一两行注释但不要每行都写。报告贴代码片段时要贴关键函数而不是整个文件并在代码下方用一两句话说明这段代码解决的是什么问题。截图要重拍而不是用旧图。常见翻车现场是报告里截图显示“库存 5”现场跑出来“库存 6”老师一对数据就知道截图是旧版。答辩前一天要把全部功能按演示顺序重新跑一遍操作完一个功能截一张图并按流程图顺序编号确保图、代码、现场演示三者完全一致。5. 代码与报告对不上答辩前常见问题的排查清单5.1 演示现场崩溃与中文乱码先查路径、编码和指针现象答辩演示时程序启动就崩溃或者中文书名显示成乱码。原因最常见的两种情况。一种是文件路径问题程序从某个固定目录启动而 books.txt 不在那里fopen 返回空指针后面直接空指针解引用另一种是编码问题源文件用 UTF-8 保存而控制台用本地代码页解析中文就变成乱码。解决演示前把数据文件路径改成绝对路径或者把 exe 和 books.txt 放在同一个目录代码里加上 setlocale(LC_ALL, )让中文输出跟随系统区域设置。如果还是乱码就把源文件统一保存成控制台兼容的编码再重新编译。这个坑我踩过两次现在每次提交前都会在干净目录里重新编译运行一遍确认换机器也能跑。5.2 “为什么用链表而不用顺序表”答不上来提前准备三句话现象答辩时老师问“你为什么用链表不用数组”只能回答“因为大家都用链表”然后冷场。原因选型时没有形成“需求 - 结构 - 理由”的链条被问到除了“别人都这么写”之外的任何问题都会卡壳。解决给每个核心数据结构准备三句话。第一句说需求“本系统需要频繁插入和删除图书图书数量不固定”第二句说结构特点“链表插入删除只需修改指针不需要搬移数据”第三句说代价“随机访问需要遍历但对于几百条图书数据遍历成本可以接受。”这三句话在报告概要设计里写一遍答辩前朗读一遍基本不会再被问倒。5.3 报告截图与现场运行结果不一致提交前强制重跑截图现象老师一边翻报告一边看演示发现报告上的库存数量和演示界面不一致当场对数据。原因报告里用的是几天前的截图改过代码之后没有重新跑或者截图时测试数据已经变了。解决把“重跑截图”列为提交前的固定工序。按演示顺序从头操作一遍每次操作后立即截图截图命名带时间戳报告引用哪张图就从 figures 目录里找哪张。这条流程并不复杂但能规避掉答辩中最尴尬的“数据对不上”情况。5.4 时间复杂度和空间复杂度被一问就懵准备一张复杂度卡片现象老师问“你这个查找函数的时间复杂度是多少”回答“O(n) 吧”再问为什么就说不清。原因只背了结论没看推导复杂度没有和代码对应起来。解决不要求严格证明但至少能说出“几层循环”和“每次循环遍历什么”。链表查找是单层循环遍历 n 个节点所以 O(n)嵌套两层循环逐个比较排序是 O(n²)哈希查找平均 O(1)最坏 O(n)。把每个核心函数的复杂度列成一张小表放在报告附录同时准备一句推导“这个函数有一个 for 循环遍历链表最多 n 次所以时间复杂度 O(n)额外空间 O(1)。”5.5 报告被指“模板味太重”把需求分析写成自己项目的语言现象报告文字和网上模板高度相似查重率高老师一看就知道是套话。原因需求分析里写的是“本系统实现了图书的增删改查”这种通用表述没有针对自己的设计写边界和假设。解决写清楚本系统的具体规则比如“本系统假设 ISBN 为 13 位数字读入时校验长度”“图书超过 1000 本时提示存储上限”“书名按控制台输入原样保存不做转义处理”。这些细节是模板没有的也是老师判断你有没有自己动脑的依据。把通用模板当成起点可以但最终文字必须是你自己项目的行为描述。6. 一个能多拿一档分的技巧用日志可视化验证设计正确性最后一个技巧适合在报告写完、答辩前还有一点时间时加上给程序加日志输出把运行数据写成 CSV 文件再用绘图脚本画成图放进报告的测试章节。这个动作成本很低但效果很明显——它把“功能跑通了”提升为“验证了设计的正确性”。做法分两步。第一步在排序或查找模块里记录耗时输出成 CSV// perf_log.cpp #include chrono // 对 n 条记录执行一次查找记录耗时微秒 auto start std::chrono::high_resolution_clock::now(); findBook(head, targetIsbn); auto end std::chrono::high_resolution_clock::now(); double cost std::chrono::durationdouble, std::micro(end - start).count(); fprintf(fp, %d %.2f\n, n, cost); // 写入 n 与耗时第二步用任意能画图的脚本读取 CSV画一条“数据量-耗时”的散点曲线。如果随着数据量翻倍查找耗时近似线性增长就验证了链表查找 O(n) 的结论如果增长非常平缓说明哈希索引起作用了。把这张图放在报告“测试与分析”一节并在图下写一句“实测耗时随数据量近似线性增长与链表查找的 O(n) 分析一致”这比单纯写“时间复杂度为 O(n)”有力得多。这个习惯我是从一次答辩里学到的。某次课程设计我做的排序比较口头分析说得头头是道老师追问“你的实测曲线呢”当场拿不出数据分数被压了一档。从那以后凡是涉及复杂度结论我都要先跑出一组真实数据再往报告里写。曲线可以不用多么光滑真实、可复现就够了它证明的不只是程序能跑更是你验证过自己的设计。希望你也能用这个方法把课程设计的分数往上再抬一档希望帮到你。本文还有配套的精品资源点击获取