C语言链表实战:航空订票系统课设代码与避坑指南

发布时间:2026/10/3 13:11:26
C语言链表实战:航空订票系统课设代码与避坑指南 简介这份数据结构课程设计文档面向计算机相关专业学生与课程设计指导教师围绕航空订票系统的完整实现展开帮助读者掌握线性表在实际业务场景中的应用。文档共1个doc文件压缩包约1.18MB内容涵盖总体设计、概要设计、详细设计、调试分析、测试数据及截图、时间复杂度分析、问题思考、算法改进设想、课设总结体会与附录源代码等模块。系统功能包括航班录入、按航班号或起降城市查询、订票与满仓候补队列、退票及余票处理、航班信息修改与文件持久化存储并给出单链表、等候订票队列等结构体定义与各模块算法说明。已有1292人学习适合需要参考完整课设方案、理解链表增删改查与队列应用、撰写实验报告或准备答辩的读者可据此快速梳理设计思路、对照源码调试并完成自己的课程设计。1. 航空订票系统课设拆包一份能跑通的 C 语言链表实战文档如果你正在做数据结构课程设计选题又恰好是航空订票系统那这份 30 页的文档大概率能帮你省下不少翻车时间。它不是那种只给题目不给代码的“空壳课设”而是把总体设计、概要设计、详细设计、调试分析、时间复杂度、源代码全部摊开写完了。核心数据结构就两块航班信息用单链表串起来每个航班下面挂两个子结构——已订票乘客链表和等候订票队列。功能覆盖录入、查询、订票、退票、修改航班信息、文件读写六个模块代码量不大但五脏俱全。适合两类人一是课设赶工期、需要一份能编译能演示的参考实现二是刚学完链表和队列、想找一个完整项目把增删改查串一遍的练手者。文档里连调试时踩过的坑都写进去了比如字符串赋值用错、break 提前跳出循环、余票只减一张这些细节比代码本身更值钱。2. 数据结构选型为什么是单链表加链队列而不是数组2.1 航班主链表的插入方式与时间复杂度取舍文档里录入模块的做法是“查找单链表的链尾在链头插入新结点”。这句话初看有点矛盾——既然要找链尾为什么又插在链头实际代码里用的是头插法p-next H-next; H-next p;新航班直接挂在头结点后面。头插的好处是插入本身 O(1)不需要遍历到尾代价是航班在链表里的顺序和录入顺序相反。对于课设演示来说浏览时能看到所有航班就行顺序反了不影响功能。但如果你想让航班按起飞时间或航班号有序排列头插就不合适了得改成尾插或者插入时排序。这里有个选型上的关键判断为什么不用数组存航班数组的优点是随机访问快查询第 i 个航班 O(1)但订票系统里航班数量是动态的今天录入 5 条明天可能加到 20 条数组扩容要么浪费空间要么频繁 realloc。链表虽然查询要 O(n)但插入删除不用搬移元素而且每个航班下面挂的乘客链表和等候队列本身就是动态结构用链表更自然。文档里时间复杂度分析写“录入为 O(1)”严格说头插确实是 O(1)但前提是你不需要先找到链尾。如果按概要设计里写的“查找链尾再插入”那就是 O(n) 了。这里文档的表述和代码有细微出入以代码为准。2.2 乘客链表与等候队列的双结构设计每个航班结点lineinfo里有两个指针Lnode *order指向已订票乘客链表的头结点linkqueue *wait指向等候替补队列。这个设计是整份课设最值得看的部分。已订票乘客用单链表存因为订票是头插、退票要按姓名和票号查找后删除链表都支持。等候队列用链队列front 和 rear 两个指针入队在队尾、出队在队头符合先来先服务。为什么不用一个链表同时管已订票和等候因为两者的操作语义不同。已订票乘客需要按票号精确查找和删除等候乘客只需要按入队顺序通知。分开之后退票逻辑就清晰了先删已订票乘客结点然后检查wait队列是否为空非空则出队一个等候乘客把他插入已订票链表余票不变如果等候队列为空才把余票加回去。文档里退票模块的流程图就是这个逻辑。这个双结构设计在课设级别算比较完整的比那些只用一个数组存乘客的实现高出一个档次。2.3 结构体定义里的字段规划与内存布局文档给出了四个结构体wat_ros等候乘客、pqueue等候队列、ord_ros订单/乘客、airline航班。字段规划有几个点值得注意。航班号air_num[7]和飞机型号plane_num[10]用定长字符数组这是 C 语言课设的常规做法好处是文件读写时格式固定、方便用fscanf和fprintf。日期拆成year[5]、month[3]、day[3]三个字段起飞和降落时间拆成qhour[3]、qminute[3]、jhour[3]、jminute[3]这样在查询和显示时可以灵活拼接也方便按日期比较。票价price和折扣zhekou用 float余票量tkt_sur和乘员定额tkt_amt用 int。这里有个隐患float 做等值比较会有精度问题但课设里票价只用于显示和简单计算不涉及精确比较所以问题不大。乘客结构体ord_ros里的票号piaohaio[20]是拼接生成的航班号 年 月 日 订票前余票量。这个设计保证了同一航班同一天不同订单的票号唯一因为余票量在变。文档里用itoa把余票量转成字符串再strcat拼接逻辑是对的但itoa不是标准 C 函数在 VC6.0 里能用换到 GCC 或 Clang 就编译不过。这是后面避坑章节要展开的点。3. 核心模块实现从录入到退票的代码拆解3.1 录入模块头插法建表与文件写入录入模块的代码在文档里叫Greatelist函数签名是int Greatelist(lineair H, int n)。注意这里用了引用传参H这是 C 的语法但文件扩展名如果是.c就会编译报错。VC6.0 对.cpp文件支持引用所以文档说“在 VC6.0 上编译运行”是前提。函数先给头结点分配空间然后循环 n 次每次分配一个航班结点用scanf读入所有字段余票量初始化为乘员定额然后头插。int Greatelist(lineair H, int n) { lineinfo *p; // 头结点分配 H (lineair)malloc(sizeof(lineinfo)); H-next NULL; H-order (linklist)malloc(sizeof(Lnode)); H-order-next NULL; H-wait (linkqueue*)malloc(sizeof(linkqueue)); H-wait-rear H-wait-front NULL; for (int i 0; i n; i) { p (lineair)malloc(sizeof(lineinfo)); // 读入航班信息注意字段顺序要和 printf 提示一致 scanf(%s%s%s%s%s%s%s%s%s%s%s%f%f%d, p-qdname, p-zhname, p-air_num, p-plane_num, p-year, p-month, p-day, p-qhour, p-qminute, p-jhour, p-jminute, p-zhekou, p-price, p-tkt_amt); p-tkt_sur p-tkt_amt; // 初始余票等于定额 p-order (linklist)malloc(sizeof(Lnode)); p-order-next NULL; p-wait (linkqueue*)malloc(sizeof(linkqueue)); p-wait-rear p-wait-front NULL; p-next H-next; // 头插 H-next p; } return 1; }参数说明H是航班链表头指针的引用n是要录入的航线条数。scanf的格式串里%s读字符串%f读 float%d读 int。注意zhekou和price是 float必须传地址p-zhekou。代码里每个航班结点都单独分配了order和wait这是对的因为每个航班的乘客和等候队列是独立的。但头结点的order和wait分配了却没用上属于冗余不影响运行但浪费一点内存。3.2 订票模块余票判断、票号生成与等候队列入队订票函数Dinpiao是整份代码里最长的。逻辑分三层先按航班号和日期查找找到后判断余票是否足够够则输入乘客信息、生成票号、头插到乘客链表、余票减去订票量不够则遍历所有航班找同月同起降城市且有余票的替代航班如果替代航班也没有就把乘客信息入等候队列。// 票号生成航班号 年 月 日 订票前余票量 char *b (char*)malloc(sizeof(char)); strcpy(q-airnum, p-air_num); itoa(p-tkt_sur, b, 10); // 余票量转字符串 strcpy(q-piaohaio, p-air_num); strcat(q-piaohaio, p-year); strcat(q-piaohaio, p-month); strcat(q-piaohaio, p-day); strcat(q-piaohaio, b); // 拼接成唯一票号 p-tkt_sur - m; // 余票减少 q-next p-order-next; // 头插到乘客链表 p-order-next q;这里有几个参数要盯住。m是订票张数必须m p-tkt_sur才允许订。itoa的第三个参数 10 表示十进制。票号拼接的顺序是航班号在前、日期在中、余票量在后这样同一航班同一天的不同订单因为余票量不同票号就不会重复。但有个边界如果两个乘客同时订票余票量在第一次订票后已经变了第二次生成的票号自然不同。课设是单机单用户不存在并发所以这个设计够用。等候队列入队的代码在文档里被截断了但根据结构体定义可以补全。qnode有name、phone、next入队就是s-next NULL; p-wait-rear-next s; p-wait-rear s;如果队列为空则front和rear都指向s。这部分文档没贴全但逻辑是标准的链队列入队。3.3 退票模块双链表查找与等候乘客通知退票的输入是乘客姓名和票号。先在航班的order链表里按姓名和票号查找找到则删除结点然后判断wait队列是否为空。非空则出队一个等候乘客把他插入order链表余票不变空则tkt_sur 退票数。// 退票核心逻辑根据文档描述补全 Lnode *pre p-order; Lnode *cur p-order-next; while (cur) { if (strcmp(cur-name, name) 0 strcmp(cur-piaohaio, ticket_id) 0) { pre-next cur-next; // 删除结点 int refund cur-dpl; free(cur); if (p-wait-front ! NULL) { // 等候队列非空出队一个乘客补位 qnode *w p-wait-front; p-wait-front w-next; if (p-wait-front NULL) p-wait-rear NULL; // 把 w 的信息插入 order 链表此处省略具体插入代码 free(w); } else { p-tkt_sur refund; // 无人等候余票加回 } return 1; } pre cur; cur cur-next; } return 0; // 未找到退票失败这里的关键参数是refund即退票张数从被删除结点的dpl字段取。等候队列出队后乘客信息要从qnode转到Lnode字段不完全对应——qnode只有姓名和电话没有证件号和订票量。文档里没展开这部分实际补全时需要给等候乘客默认订票量 1或者要求等候时也输入证件号。这是课设代码的一个粗糙点但不影响演示退票主流程。3.4 文件读写链表与磁盘数据的同步文件模块分两个方向启动时从文件读入航班和乘客信息建链表退出时把链表写回文件。文档里只给了写入的流程图读取部分没展开。写入逻辑是遍历航班链表对每个航班先写航班信息再遍历order链表写乘客信息。格式必须严格固定否则读取时fscanf会错位。// 写入航班和乘客信息 void SaveToFile(lineair H, FILE *fp) { lineinfo *p H-next; while (p) { fprintf(fp, %s %s %s %s %s %s %s %s %s %s %s %.1f %.1f %d %d\n, p-qdname, p-zhname, p-air_num, p-plane_num, p-year, p-month, p-day, p-qhour, p-qminute, p-jhour, p-jminute, p-zhekou, p-price, p-tkt_amt, p-tkt_sur); Lnode *q p-order-next; while (q) { fprintf(fp, %s %s %s %d %s\n, q-name, q-IDnum, q-airnum, q-dpl, q-piaohaio); q q-next; } p p-next; } }参数说明fp是已打开的文件指针模式为w或a。航班信息一行乘客信息每个一行用换行符分隔。读取时先读一行判断是航班还是乘客或者用固定字段数区分。文档里提到“文件检测函数运用错误”导致程序终止常见原因是fopen返回 NULL 没判断或者feof用法不对。后面避坑章节会展开。4. 避坑与排查课设代码里那些让人调半天的错误4.1 字符串赋值用成编译不报错但运行崩溃文档调试分析里明确写了“在一个字符串的复制中使用了赋值调试过程指出错错误半天都不知道改”。C 语言里字符数组不能用直接赋值char a[20]; a hello;编译会报错但如果是char *a; a hello;编译通过运行时如果后面试图修改a指向的内容就会崩溃。课设里常见的是结构体里的字符数组比如p-name q-name这种错误编译器会直接报“赋值给数组类型”但如果是通过指针操作就可能绕过编译检查。解决方法是统一用strcpy或strncpy并且确保目标数组足够大。4.2break放在查找循环里只能查到第一条文档里写“查询信息只能查询链表中的第一条航线检查程序原来是多用了 break 造成过早跳出循环”。查找逻辑通常是while (p) { if (匹配) { 输出; break; } p p-next; }如果break写在了if外面或者循环里有多余的break就会在第一次迭代后直接跳出。更隐蔽的情况是if里用了return导致函数提前返回。排查方法是把查找循环单独拎出来用 printf 打印每次比较的航班号看循环是否真的遍历完了。如果只打印了第一条就停那就是break或return位置不对。4.3 余票只减一张订多张时数据不对文档里写“乘客订多张票后浏览信息发现余票只减了一张检查程序发现乘客订票后只对余票做了自减”。p-tkt_sur--和p-tkt_sur - m是两回事。如果订票量m是 3自减只减 1余票就多了 2。这个错误在测试时如果只订 1 张票不会暴露一旦订多张就翻车。解决方法是所有涉及数量的更新都用-或并且在做减法前判断m p-tkt_sur。另外退票时加回余票也要用 refund不能只加 1。4.4 文件写入后不重新写回余票还是初始值文档里写“运行程序后打开所写的文件发现航班信息的余票量没有随乘客的订票而减少还是初始值”。原因是订票操作只改了内存里的链表没有在每次订票后重新写文件。文件写入只在退出系统时执行一次如果程序异常终止或者没走正常退出流程文件就不会更新。解决方法是每次修改链表后立即调用保存函数或者至少在订票、退票、修改航班后都触发一次写文件。代价是频繁 IO 会慢但课设数据量小可以接受。4.5itoa不是标准函数换编译器就报错文档代码里用了itoa(p-tkt_sur, b, 10)这个函数在 VC6.0 的stdlib.h里有但 GCC 和 Clang 不提供。如果你把代码复制到 Dev-C、Code::Blocks 或 VS Code MinGW 里编译会报“undefined reference to itoa”。替代方案是用sprintf(b, %d, p-tkt_sur)标准 C 都支持。另外getch()也不是标准函数需要conio.h在非 Windows 环境同样不可用。如果课设要求跨平台这两个点必须改。5. 进阶改造把课设代码变成能写进简历的项目5.1 用sprintf替换itoa让代码在 GCC 下编译通过原始代码依赖 VC6.0 的itoa和getch换到现代编译器直接翻车。改造第一步就是替换这两个函数。itoa换成sprintfgetch换成getchar或者用scanf读密码。密码不回显的需求可以用getch在 Windows 下实现但跨平台可以用termios在 Linux 下关回显代码量稍大。课设演示如果只在 Windows 下跑保留getch也行但至少把itoa换掉因为sprintf更通用。// 替换 itoa char b[10]; sprintf(b, %d, p-tkt_sur); strcat(q-piaohaio, b);sprintf的第二个参数是格式串第三个是整数。b要预留足够空间tkt_sur最大是乘员定额一般不超过 4 位数char b[10]够用。5.2 给航班链表加排序把查询从 O(n) 降到 O(log n)原始代码的查询是遍历单链表时间复杂度 O(n)。文档在“算法的改进设想”里提到可以按起飞抵达城市排序然后用分块查找。更实际的做法是建一个按航班号排序的索引数组或者直接用二叉搜索树存航班。但课设代码改动量最小的方案是录入时用尾插 插入排序保持链表按航班号有序查询时用二分查找。不过链表二分查找需要随机访问链表做不到所以要么改成数组 链表混合要么用跳表。对于课设级别更简单的是把航班信息读进数组用qsort排序后二分查找找到后再操作链表。这样查询 O(log n)插入删除还是 O(n)但查询是高频操作收益明显。5.3 用文件持久化验证数据一致性改造后的代码要验证文件读写是否真的同步。方法很简单录入 3 条航班订 2 张票退 1 张票然后退出程序重新运行浏览所有航班和乘客信息看余票和订单是否和退出前一致。如果不一致检查保存函数是否在每次修改后都调用了以及读取函数是否正确解析了文件格式。常见问题是写入时用了fprintf带空格分隔读取时用fscanf的格式串不匹配导致字段错位。建议写入和读取用同一套格式串或者干脆用二进制模式fwrite/fread直接写结构体但结构体里有指针不能直接写需要先序列化。// 验证数据一致性的测试流程 // 1. 运行程序录入 3 条航班 // 2. 对第 1 条航班订 2 张票 // 3. 对第 1 条航班退 1 张票 // 4. 退出程序 // 5. 重新运行浏览航班和乘客 // 6. 检查第 1 条航班余票是否 定额 - 2 1 // 7. 检查乘客链表是否还有 1 个订单这个测试能覆盖录入、订票、退票、文件写入、文件读取五个模块。如果第 6 步余票不对说明退票时余票加回逻辑有问题如果第 7 步订单数不对说明退票删除结点或文件写入有问题。我一般会在每次改完代码后强制走一遍这个流程比单步调试快。5.4 把等候队列的通知逻辑补完整原始代码里等候队列的入队和出队只写了一半退票时通知等候乘客的部分被截断了。补全的思路是等候乘客入队时存姓名和电话退票时从队头取一个然后要求这个乘客补充证件号和订票量再插入已订票链表。如果等候乘客联系不上就继续取下一个直到队列空或有人确认。课设演示可以简化成出队后直接按默认订票量 1 插入票号用当前余票量生成。这样退票后余票不变等候乘客自动补位逻辑闭环。从那以后我每次拿到课设代码都先做三件事把非标准函数替换掉、把文件读写跑一遍、把边界条件空链表、满仓、重复票号测一遍。这三步走完基本不会在答辩现场翻车。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询