news 2026/9/8 3:42:11

数据结构课程设计:用栈和队列实现停车场管理系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
数据结构课程设计:用栈和队列实现停车场管理系统

简介:面向高校数据结构课程的一份完整课程设计资源——停车场管理程序,适合正在完成大作业或希望将理论用于实践的学生。项目围绕车辆进出管理、车位查询与状态更新等真实场景,综合运用数组、链表、栈、队列、哈希表及排序搜索等数据结构与算法,展示从数据组织到系统实现的完整思路。资源共42个文件,以cpp源代码、exe可执行程序、pdb调试符号、ipch/obj编译中间文件及db数据文件为主,整体约15.07MB,附带Visual Studio工程文件与解决方案,便于直接打开运行和二次修改。目前已有447人学习下载,对想参考完整项目结构、调试过程或代码组织方式的读者很有帮助。通过研读源码和运行效果,可以直观体会不同数据结构在停车位管理、排队调度和快速检索中的实际作用,也能借鉴模块化设计方法提升自己的程序可读性与可维护性。 校园里流传着一句话:数据结构这门课,平时学得云里雾里,到了大作业验收那一刻,才知道自己到底行不行。我当年选题时一眼就挑中了“停车场管理程序”,原因很朴素——比起迷宫、八皇后、哈夫曼树这些经典题,停车场问题离生活最近:入场、出场、排队、缴费,每一步都能在脑子里面还原成画面,实现的时候心里有底,调试的时候也容易判断“程序跑得对不对”。

但真做起来才发现,这个题远不是“写个链表、模拟进出”那么简单。它几乎把线性表的核心操作全考了一遍:栈用来模拟车道内的车辆堆叠,队列用来模拟便道上的等待序列,中间还穿插着车辆临时挪车、按入场时间计费这些逻辑。做完这道题,你基本就把《数据结构(C语言版)》里线性结构那一整章的考点串起来了。

我把自己当时从需求分析、结构设计、编码到写实验报告的完整过程整理成本文。代码采用C语言实现,参考严蔚敏版教材的思路,尽量贴近课程设计验收的标准。不管是被人推荐选了这道题、还是正在被度日如年折磨的同学,这篇文章希望能帮你少走几晚弯路。

1. 从题目要求到需求建模:先想清楚程序到底要做什么

很多人的第一个坑,是没读懂题目就急着敲代码。停车场管理程序的原型,几乎都出自严蔚敏《数据结构》教材的习题,标准描述大概是这样的。

1.1 原始题目的场景还原

有一个停车场,只有一个窄长的通道,最多容纳N辆车。通道是单行的,先进的车停在里面,后进的车停在前面。这意味着:如果里面的车要出去,后面所有车都得先为它让路,一辆一辆临时挪出去,等它开走后再按原顺序倒回来。

停车场外只有一条便道,当停车场满了的时候,后来的车只能在便道上排队等待。一旦停车场有车离开,便道上的第一辆车就进场补位。

当车辆离开时,需要按它在停车场内停留的时间缴纳费用。教材里通常设定一个单位时间的收费单价(比如每小时X元)。

1.2 考虑边界细节后得到行为列表

把这段场景描述翻译成程序需求,至少包含以下几条行为规则:

  • 停车场按照到达时间先后顺序停车,后进车辆在通道入口一侧。
  • 当某辆车要离场时,若它不是通道入口处的第一辆,则它前面(即通道入口侧)的所有车辆必须暂时离开停车场,按原顺序暂存在旁边某个区域,然后目标车辆离开,暂存车辆再按原顺序返回停车场。
  • 停车场满时,新到达车辆在便道(等待区)排队。
  • 停车场有空位时,便道第一辆车进入停车场。
  • 每次车辆离场时,打印计费信息。

1.3 需求建模中容易被忽略的三个点

第一,车辆“按原顺序返回”意味着什么?如果暂存区用栈B,那么从停车场栈A中弹出车辆压入栈B,再弹回栈A,顺序刚好恢复,这就是栈的经典特性——后进先出天然支持“倒车腾挪”。

第二,便道排队的车辆进入停车场的时机是什么?是“任意一辆车离场后,便道头车立刻进场”,而不是等到下一次“到达事件”才处理。如果不加这个细节,排队车辆就会一直傻等。

第三,输入输出格式。大多数题目会规定用“到达/离开 + 车牌号 + 时间”的格式输入,例如A car1 8:30表示车牌car1在8点30分到达,D car1 10:15表示car1在10点15分离场。如果题目没规定,建议自己约定并写进实验报告,这也是加分项。

2. 为什么这个题是栈+队列的绝配:结构选型的推导逻辑

选对数据结构,等于这题做完了一半。但“选对”不是拍脑袋,而是从问题特性里推出来的。

2.1 停车场通道的行为特征:后进先出

停车场是单通道,只允许在通道一端进出车辆。后到的车停在出口侧,先到的车停在通道最里面。当最里面的车要离开时,它前面的车必须“临时退出”,这个“临时退出+原序返回”的模型,完全对应栈的LIFO(Last In First Out)特性。

如果用数组模拟,也能写,但你要手动维护一个top指针,还要在中间删除元素后依次移动数组,代码量和出错概率都会上升。用栈的意义在于:把“后进先出”这个规则交给数据结构本身去保证,你的人脑只需要关注业务逻辑。

2.2 便道等待区的行为特征:先进先出

便道上的车按到达顺序排队,最早到达的优先进入停车场,对应队列的FIFO(First In First Out)特性。如果这里用栈,会出现后到的车先进场的荒谬场景。

2.3 临时让路区的行为特征:也是栈

暂存“让路车辆”的区域,其实也是一个栈。目标车辆前面那些车,从停车场栈顶弹出,依次压入临时栈;目标车辆离场后,再从临时栈弹回停车场栈。整个过程是对栈的二次使用,却不需要额外定义新结构,理解了这一点,实现时思路会非常清晰。

2.4 三种操作的时间复杂度推演

  • 车辆入场:停车场栈push,O(1);若满则入队,O(1)。
  • 车辆离场:平均情况下,需要移动k辆车(k≤停车场容量N),每辆车经历一次pop+push+pop+push,时间复杂度O(N)。N通常是一个较小常量,可接受。
  • 计费:根据车辆进出时间差计算,O(1)。

对比一下:如果完全不使用栈和队列,用链表硬模拟“车辆位置”,你需要频繁查询“某辆车在哪”“它前面有谁”,每次离场都要遍历整个链表找车的位置,复杂度O(N)起步且代码逻辑繁重。用栈+队列之后,每个车辆的物理位置天然对应一个“内存位置”,查车逻辑大幅简化。

3. 核心数据结构与模块接口:让代码结构一眼就能在答辩时讲清楚

本轮实操我采用纯C语言实现,因为课程设计通常要求C语言,且严蔚敏教材配套的参考代码都是C风格。如果你所在学校允许C++,可以用STL的stackqueue,逻辑完全一致,但建议先理解底层原理,答辩时老师追问“底层怎么实现的”才答得上来。

3.1 停车场栈的定义

#define MAX_SIZE 5 // 停车场最大容量,按题目要求调整 typedef struct { char plate[16]; // 车牌号,简单用字符串 int hour; // 入场时间(小时) int minute; // 入场时间(分钟) } Car; typedef struct { Car data[MAX_SIZE]; int top; // 栈顶指针,初始为-1 } ParkingStack;

这里的top指向当前栈顶元素。空栈时top=-1,满栈时top=MAX_SIZE-1。注意:我故意设计成用数组实现栈,而不是链栈。原因很简单:停车场容量固定,数组栈不需要频繁malloc/free,代码更直观,也更好在实验报告里画图解释。如果你想展示链表功底,用链栈也完全可以,只是需要额外处理节点释放。

3.2 便道等待队列的定义

typedef struct QueueNode { Car data; struct QueueNode *next; } QueueNode; typedef struct { QueueNode *front; // 队头指针 QueueNode *rear; // 队尾指针 } LinkQueue;

我用链式队列而不是顺序队列来表示便道,因为便道长度理论上不限(现实中也可能排长队),链式队列天然支持动态增长,也顺便向老师展示了你对“队列两种存储结构”的掌握。

3.3 按时间计费的关键函数

计费逻辑其实很简单,但容易踩坑:时间差计算要按照绝对值算,而不是简单地用“离开小时 - 到达小时”。比如车辆到达时间是23:50,离开时间是00:20,这种跨午夜的情况,如果只取小时相减会得到负值。稳妥做法是把时间统一换算成分钟再相减。

int calculate_fee(Car car, int leave_hour, int leave_minute, int price_per_hour) { int arrive_total = car.hour * 60 + car.minute; int leave_total = leave_hour * 60 + leave_minute; // 若跨天,则加 24 小时 if (leave_total < arrive_total) { leave_total += 24 * 60; } int duration = leave_total - arrive_total; // 停留分钟数 // 让利:不足一小时按一小时计费,或按实际分钟计费,取决于题目要求 int hours = (duration + 59) / 60; // 向上取整到小时 return hours * price_per_hour; }

这里的向上取整是常规处理。如果题目要求“不足一小时不收费”或“按分钟精确计费”,改一行就行,实验报告里可以针对这个边界写测试用例。

3.4 主流程控制:到达和离开的统一入口

主程序读入操作命令,按字符分支处理:

int main() { ParkingStack lot; LinkQueue waiting; init_stack(&lot); init_queue(&waiting); char op; char plate[16]; int hour, minute; while (scanf("%c %s %d:%d", &op, plate, &hour, &minute) == 4) { if (op == 'A') { handle_arrive(&lot, &waiting, plate, hour, minute); } else if (op == 'D') { handle_depart(&lot, &waiting, plate, hour, minute); } else if (op == 'E') { break; // 结束命令 } } return 0; }

注意scanf格式中的%d:%d,输入时间时中间有冒号,格式串要与之匹配。很多同学在这里卡了半天,输入一直读不进去,检查一下格式串多半就解决了。

4. 进场和离场的完整实现:把逻辑翻译成代码

4.1 到达处理:能进就进,不能进就排队

一车辆到达时,先判断停车场是否已满。未满就直接入栈,满则入便道队列。

void handle_arrive(ParkingStack *lot, LinkQueue *waiting, char plate[], int hour, int minute) { Car c; strcpy(c.plate, plate); c.hour = hour; c.minute = minute; if (lot->top == MAX_SIZE - 1) { // 停车场满,进入便道排队 enqueue(waiting, c); printf("%s 进入便道等待\n", plate); } else { push(lot, c); printf("%s 进入停车场,位置 %d\n", plate, lot->top + 1); } }

这段逻辑看似简单,但有个细节值得注意:入便道的车不需要在这里判断“便道是否满”,因为链式队列不会满。如果你用顺序队列实现便道,就需要额外处理队满的情况,这也是我推荐链式队列的原因之一。

4.2 离开处理:临时挪车、计费、便道补位

离场是这个程序的灵魂。处理顺序是:

  1. 在栈中找到目标车辆的位置(从栈顶往下找)。
  2. 从栈顶到目标车辆的前一辆,依次弹出并压入临时栈。
  3. 弹出目标车辆,计费并输出。
  4. 将临时栈中的车辆依次弹回停车场栈。
  5. 若便道队列非空,则队头车辆出队并停入停车场栈。
void handle_depart(ParkingStack *lot, LinkQueue *waiting, char plate[], int hour, int minute) { ParkingStack temp; init_stack(&temp); // 查找目标车辆位置 int found = -1; for (int i = lot->top; i >= 0; i--) { if (strcmp(lot->data[i].plate, plate) == 0) { found = i; break; } } if (found == -1) { printf("未找到车辆 %s,请检查车牌号\n", plate); return; } // 把目标车辆上面的车挪到临时栈 while (lot->top > found) { Car tmp = pop(lot); push(&temp, tmp); } // 弹出目标车辆 Car leaving = pop(lot); int fee = calculate_fee(leaving, hour, minute, 2); // 每小时收费2元 printf("%s 离场,停车时长 %d 分钟,费用 %d 元\n", plate, (hour * 60 + minute) - (leaving.hour * 60 + leaving.minute), fee); // 临时栈车辆归位 while (temp.top != -1) { Car tmp = pop(&temp); push(lot, tmp); } // 便道补位 if (!is_queue_empty(waiting)) { Car next = dequeue(waiting); push(lot, next); printf("%s 从便道进入停车场\n", next.plate); } }

4.3 这段代码容易翻车的三个细节

第一,found的语义。我在循环里找的是数组下标,但栈顶是top。判断“目标车辆在栈中的位置”时,plate==data[i]要遍历整个栈。链栈实现时更要注意,链表只能从头节点顺序遍历,逻辑类似但代码不同。

第二,临时栈的大小。临时栈最大只会存MAX_SIZE-1辆车(目标车辆上面的所有车),所以可以复用MAX_SIZE作为数组大小,不会溢出。但如果你动态分配临时栈并忘记释放,程序虽然能跑,检查内存泄漏时会很难看。

第三,计费时刻的边界。这里计算的是目标车辆实际离场时间,而不是它“开始处理”的时间。如果你把挪车过程产生的耗时也算进停车时间,逻辑就会出错。

5. 实验报告加分项与测试用例设计

数据结构大作业的价值,一半在代码,一半在实验报告。一个能跑的马马虎虎的程序只够及格,但一份结构完整、测试充分的报告能帮你从“良”跳到“优”。

5.1 必测场景:满栈、跨天、无牌

  • 满栈后继续来车,观察车辆是否进入便道。
  • 有一辆车离场后,便道第一辆车是否自动进场。
  • 目标车辆不是栈顶时,挪车操作后顺序是否正确恢复。
  • 跨午夜停车(23:50进、00:20出),计费是否正确。
  • 离场车辆的车牌号不在停车场内,程序是否能优雅报错而不是崩溃。

5.2 测试输入示例

假设停车场容量为3,收费每小时2元:

A 京A123 8:00 A 京A456 8:10 A 京A789 8:20 A 京A000 8:30 D 京A456 9:00 E

运行结果预期:

京A123 进入停车场,位置 1 京A456 进入停车场,位置 2 京A789 进入停车场,位置 3 京A000 进入便道等待 京A456 离场,停车时长 50 分钟,费用 2 元 京A000 从便道进入停车场

注意:由于停车场容量为3,第4辆车到达时停车场已满,进入便道。当京A456离场后,京A000立即从便道入场。这个输出序列是验证程序正确性的关键。

5.3 实验报告结构建议

一份能让老师少问三句的报告,建议包含:

  • 问题描述与需求分析:用自己的话复述题目要求,列出功能点。
  • 数据结构设计:画出栈、队列的逻辑结构图(用文字或简单示意图),说明为什么选数组栈和链式队列。
  • 核心算法与流程图:画出离场处理的流程图(文字描述即可),重点说明挪车顺序。
  • 测试与运行结果:附上输入输出截图或文本,说明每个用例覆盖了什么边界情况。
  • 总结与心得体会:写你在调试中遇到的问题和最终解法。

我在报告里还加了一个小节叫“设计权衡”,对比了数组栈vs链栈、顺序队列vs链式队列在本问题中的优缺点,老师当场多看了两眼,我觉得是加分点。

6. 从课程设计到工程认知:这道题教会我的三件事

做完这个项目,回头看,收获不只是会用栈和队列,而是对“数据结构是算法的骨架”这句话有了实感。

6.1 选择数据结构的标准不是“我会哪个”,而是“哪个匹配规则”

停车场通道的特点是后进先出,便道的特征是先进先出,这两个规则一确定,数据结构基本就锁定为栈和队列。现实中很多系统的数据结构设计也是这个思路:先梳理业务规则,再确定数据结构,最后才写代码。顺序颠倒的话,后面全是坑。

6.2 调试时要能“手动模拟程序”,而不是依赖print堆输出

初期我的程序总在挪车顺序上出问题,输出结果混乱。后来我拿一张纸画出停车场栈的每一步变化,手动跑了一遍输入数据,马上发现问题出在“临时栈车辆归位时循环条件写错了”。手动画栈、画队列,是这个题目最好的调试方式。

6.3 代码的可读性比“炫技”重要

我最初想用「栈套队列」之类的复合结构来展示实力,后来发现简单直接的结构最容易让答辩老师看懂,也最容易验证正确性。合理命名变量、把处理逻辑拆成handle_arrivehandle_depart两个函数、注释写到点子上,这些习惯比多用一个高级数据结构更有价值。

如果你正在为数据结构大作业发愁,建议找一个下午,拿一张纸,先把停车场栈和便道队列的每一步变化画熟,再打开IDE写代码。思路顺了,代码其实很薄;思路不顺的话,写出来的代码只是在为混乱的逻辑买单。这道题做完,你对“线性结构”这一章的理解,会比刷十道习题都深。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 3:41:53

DeepSeek Harness 架构解析、部署实战与插件开发指南

DeepSeek Harness 这个名字最近在开发者圈子里出现频率不低。如果你看到"Harness"不知道具体是什么&#xff0c;也不确定它跟直接调 DeepSeek API 有什么区别&#xff0c;更不清楚插件管理器、桌面端应用、Skills 这些概念该怎么落到自己的项目里&#xff0c;那这篇文…

作者头像 李华
网站建设 2026/9/8 3:41:31

YOLOv5+PyQt5:从detect.py到可交付的可视化目标检测工具

简介&#xff1a;面向需要快速构建目标检测可视化应用的开发者&#xff0c;这一资源将YOLOv5的检测能力与PyQt5界面相结合&#xff0c;实现从视频文件、摄像头采集到实时输出边界框位置与类别标签的完整流程。压缩包共132个文件&#xff0c;大小108.25MB&#xff0c;涵盖34个Py…

作者头像 李华
网站建设 2026/9/8 3:41:16

STM32 RS485通信实战:原理、硬件设计、代码与工业布线避坑指南

RS485 这个标准从 1983 年发布算起&#xff0c;比很多编程语言都“老”&#xff0c;但今天去任何一座工厂、一栋写字楼、一个配电房&#xff0c;依然到处是它的影子。变频器、伺服驱动器、温控表、电表、PLC、门禁控制器&#xff0c;几乎所有工业设备的说明书里&#xff0c;第一…

作者头像 李华
网站建设 2026/9/8 3:38:39

1992年NBA选秀悬案:奥尼尔电话失联背后的决策风险

1992年NBA选秀夜&#xff0c;奥兰多魔术用状元签选中沙奎尔奥尼尔&#xff0c;这件事本身没有争议。真正让老球迷念念不忘的&#xff0c;是那通“迟到”的电话。按照流传多年的说法&#xff0c;魔术队在现场准备联系奥尼尔时&#xff0c;电话竟然失联&#xff0c;直到倒计时最后…

作者头像 李华
网站建设 2026/9/8 3:38:26

西门子S7-200smart恒压供水一拖五系统设计与调试全解析

1. 项目缘起与整体方案思路做供水控制这么多年&#xff0c;恒压供水这个场景我接触过不少&#xff0c;但真正要把“一拖五”的大泵结构做扎实&#xff0c;还得兼顾小泵补压、软启交替、休眠唤醒这套完整逻辑&#xff0c;确实不是随便写几段梯形图就能交差的。这次的项目标题里写…

作者头像 李华
网站建设 2026/9/8 3:37:44

Ventoy 多系统启动盘制作指南:一个U盘搞定PE与ISO镜像管理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华