☰
用栈和队列实现停车场管理:数据结构课设核心解析
2026/10/10 16:58:39 网站建设 项目流程

简介:面向数据结构课程学习者的停车场管理程序大作业资源包,围绕车辆进出管理、车位查询与状态更新等真实业务场景,综合运用栈、队列、哈希表、链表等经典结构设计调度逻辑,适合用作课程设计参考或算法练手项目。压缩包内共42个文件,以cpp源码、sln/vcxproj工程文件为主,附带编译生成的exe可执行程序、pdb调试符号及obj中间文件,便于直接运行和二次调试,整体大小约15.07MB。已有507人学习下载。资源内含完整Visual Studio解决方案,打开工程即可查看模块化代码结构,可执行文件能直观演示停车场出入流程;源码中针对多种数据结构做了方案对比,有助于理解不同结构在时间与空间上的取舍。对于正在完成同类数据结构大作业、需要参考完整项目实现的学生,这份资源可提供从设计到编码落地的全流程参考。

1. 数据结构大作业里的停车场管理程序:为什么这道“简单题”年年有人在答辩翻车

数据结构大作业里的停车场管理程序,几乎是每届课设出现率最高的选题之一。题目描述很“业务”:一辆车到达、满了就在便道排队、离场按时间交费,看起来用数组加一堆 if 也能写;但真正拉开分数的是你有没有把这道题拆成数据结构问题——场内车辆是后进先出,便道车辆是先进先出,离场时还要用辅助结构倒库。很多同学的代码跑得通,答辩时却答不上来“你的栈和队列分别在哪”。这篇文章按我的做法把它拆开讲:抽象模型、选型、核心代码、避坑,最后给一份答辩时能加分的进阶方案,适合第一次写课设的新手,也适合想把这题写严谨的老手。

2. 把题面翻译成结构:场内栈、便道队列与计费模型

2.1 最常见的题面版本:狭长通道、一条便道、一份计费规则

这道大作业最经典的题面大概是这样:设停车场是一个可停放 n 辆车的狭长通道,只有一个出入口,车辆按到达的先后顺序依次停入;若场内已满,后来的车只能在门外的便道上排队等候;一旦有车离场,便道上的第一辆车就进入停车场;每辆车离场时按停车时长缴纳费用。不同学校的版本会在 n 的取值、计费单价、便道是否限长上有差异,但骨架是一样的。

拆开看,这道题在数据结构层面只有三件事。第一件,停在场内的车:后来的车会堵住先来的车,先走的一定是刚进来的那批,这是严格的后进先出,对应栈。第二件,便道上等待的车:先到的排在前面,有车位时最先入场,这是先进先出,对应队列。第三件,计费:入场时刻和离场时刻都已知,按分钟差算钱,跟栈和队列没有直接关系,属于业务逻辑。

常见误区是把“车”当成唯一主角,用一个动态数组记录所有车辆,入场就 append,离场就 remove,场内场外混在一个集合里。代码确实能跑,但丢失了两个考点:栈的“出口受限”和队列的“排队顺序”。另一个误区是场内虽然用了数组,却按“找空位”的方式随机停,完全无视“必须从出口倒出去”的物理约束。这两类写法在答辩时都很容易被追问到漏洞。

2.2 场内用顺序栈、便道用链队列:选型对比与理由

明确了三个对象,下一步是给每辆车选容器。我把几个候选结构和它们的适配度放在一起对比,这也是课设报告里“数据结构设计”一节可以直接用的素材:

数据结构模拟对象关键操作选型理由不选的理由
顺序栈场内停车场入栈、出栈、取栈顶场内容量固定,数组实现简单、调试直观、无需逐车 malloc若容量不确定才需要链栈
链栈场内停车场入栈、出栈动态扩容灵活本题容量由题面给定,链栈徒增内存管理负担
循环队列便道等待区入队、出队、判满便道若由题面限定长度,用顺序循环队列很合适长度不定时会造成假溢出或浪费
链队列便道等待区入队、出队、判空便道长度天然不固定,链队列入队出队都是 O(1),不预设上限需要自己处理节点释放,代码略多

我的习惯是场内用顺序栈,便道用链队列。场内容量 n 是题目写死的,顺序栈只需要一块连续数组加一个 top 指针;便道理论上可以排无限多辆车,链队列每次入队分配一个节点,不需要提前猜上限。如果题目明确写了“便道最多只能停 5 辆”,那就把链队列换成循环队列,front、rear 加一个 count,出队入队改成取模下标,其余逻辑不变。这道题的考点之一,就是你能不能根据题面限制做出这个选择。

这里有一个细节值得在报告里写清楚:栈和队列在本质上是“受限的线性表”。停车场程序练的不是线性表本身,而是同一份事件流里,让两个受限结构各管一段数据、再通过“离场补位”完成协作。评分老师想看到的正是这种协作,而不是你写了多少个功能函数。

2.3 数据模型定版:车辆结构体与栈/队列的 C 表示

我一般先把数据模型写死,再写业务逻辑。下面是结构体定义和初始化函数,这个版本兼容绝大多数题面:

#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_PARK 3 /* 停车场容量,按题面修改 */ #define PLATE_LEN 16 /* 车牌号缓冲长度 */ typedef struct { char plate[PLATE_LEN]; /* 车牌号,定长数组 */ int arriveMin; /* 入场时刻,距 0 点的分钟数 */ } Car; typedef struct { Car slots[MAX_PARK]; /* 车位数组 */ int top; /* 栈顶指针,指向下一个空位 */ } ParkStack; typedef struct WaitNode { Car car; /* 等待的车辆 */ struct WaitNode *next; /* 指向队尾方向的下一个节点 */ } WaitNode; typedef struct { WaitNode *front; /* 队头 */ WaitNode *rear; /* 队尾 */ int count; /* 队列长度 */ } WaitQueue; void initPark(ParkStack *park) { park->top = 0; } void initQueue(WaitQueue *queue) { queue->front = queue->rear = NULL; queue->count = 0; }

几个设计决策需要说明。车牌用定长 char 数组而不用 char*,是因为每辆车入栈入队时都要拷贝整份 Car 结构,定长数组拷贝省心,不需要额外管理字符串内存;代价是车牌超过 PLATE_LEN-1 个字符会被截断,所以后面命令行解析时我会限制读入宽度。arriveMin 统一转换成“从 0 点开始的分钟数”,小时和分钟只在打印时还原,这样计费就是整数减法,避免字符串时间比较的麻烦。ParkStack 的 top 从 0 开始,语义是“下一个空位的下标”,判空、判满、入栈、出栈全部围绕这一个语义展开,这是整份代码不出下标错误的基础。

WaitQueue 里我额外加了一个 count 字段,作用是让判空、取队列长度、遍历打印都变成 O(1),不用每次从头数节点。front 和 rear 在链队列里必须同时维护:入队改 rear 的 next,出队改 front,两个指针都指向 NULL 时队列为空。这些看起来是细节,但评阅老师看代码时第一眼看的就是结构体定义和指针维护正不正确。

3. 核心实现:入场、倒库换位与便道补位怎么落代码

3.1 入场:不满就停进车位,满了就排到便道

入场逻辑是整道题最直白的一段:停车场没满,直接把车压进栈;满了,就把车挂到便道队列尾部。我不在这里做任何额外判断,因为“是否满员”本身就是要交给栈和队列去回答的问题。

void enqueue(WaitQueue *queue, Car car) { WaitNode *node = (WaitNode *)malloc(sizeof(WaitNode)); node->car = car; node->next = NULL; if (queue->rear == NULL) { queue->front = queue->rear = node; } else { queue->rear->next = node; queue->rear = node; } queue->count++; } void enterPark(ParkStack *park, WaitQueue *queue, Car car) { if (park->top < MAX_PARK) { park->slots[park->top] = car; park->top++; printf("[入场] %s %02d:%02d 停入车位%d\n", car.plate, car.arriveMin / 60, car.arriveMin % 60, park->top); } else { enqueue(queue, car); printf("[排队] %s %02d:%02d 场内已满,进入便道\n", car.plate, car.arriveMin / 60, car.arriveMin % 60); } }

enqueue 是标准的链队列尾插法:新节点总是接在 rear 后面,然后 rear 后移;如果队列原本为空,front 和 rear 同时指向新节点。注意 malloc 之后我没有判空,这是课设代码的常见妥协,严谨的做法是 malloc 返回 NULL 时报错退出,但教学演示场景一般不会触发,你可以按自己老师的规范补上。enterPark 里入栈用的是slots[top++] = car,这行代码同时完成“写入”和“top 后移”,和 2.3 节约定的 top 语义严格一致。车位编号打印用了 top+1 的值,是为了让输出从 1 开始,跟现实停车场的“1号位、2号位”对应。

这里有个容易被追问的设计点:同一辆车如果重复到达怎么办?我一般不做幂等处理,因为题面的事件序列默认是合法的。如果你要防,可以在 enterPark 里先遍历栈和队列,发现同名车牌就拒绝并打印错误,代价是入场从 O(1) 变成 O(n)。就课设而言,这个功能属于加分项,不是必选项。

3.2 出场倒库:辅助栈保序的下标计算

离场才是这道题的核心算法。目标车可能停在栈的任意位置,它上面的车必须一辆一辆挪走,它才能从出口出去;挪走的车还要按原来的顺序放回来。这个动作叫倒库,和现实里停车场最里边的车要出去时,管理员把外面的车一辆辆开出来再开回去是同一件事。

提示:倒库的下标计算是全文最容易被扣分的地方。动手写循环之前,先把 top 的定义钉死:top 是下一个空位下标,栈顶元素在 slots[top-1]。

void leavePark(ParkStack *park, WaitQueue *queue, char *plate, int leaveMin) { int pos = -1; for (int i = 0; i < park->top; i++) { if (strcmp(park->slots[i].plate, plate) == 0) { pos = i; break; } } if (pos < 0) { if (removeFromQueue(queue, plate)) { printf("[离场] %s 正在便道排队,直接离开,不产生停车费\n", plate); } else { printf("[错误] %s 不在停车场,也不在便道\n", plate); } return; } ParkStack tmp; tmp.top = 0; while (park->top > pos + 1) { park->top--; tmp.slots[tmp.top] = park->slots[park->top]; tmp.top++; } Car leaving = park->slots[park->top - 1]; park->top--; int mins = leaveMin - leaving.arriveMin; if (mins < 0) { printf("[警告] 离场时刻早于入场时刻,按0分钟计\n"); mins = 0; } int fee = ((mins + 59) / 60) * PRICE_PER_HOUR; printf("[离场] %s 停车%d分钟,费用%d元\n", leaving.plate, mins, fee); while (tmp.top > 0) { tmp.top--; park->slots[park->top] = tmp.slots[tmp.top]; park->top++; } if (queue->count > 0) { Car next = dequeue(queue); next.arriveMin = leaveMin; park->slots[park->top] = next; park->top++; printf("[补位] %s 从便道进入车位%d\n", next.plate, park->top); } }

我来拆解这段代码里最关键的循环:while (park->top > pos + 1)。假设场内顺序是 A1001 在最底、B2002 在中间、C3003 在栈顶,top 为 3,目标是让 A1001 出去,pos 为 0。第一次循环 top=3 > 1,于是 top 降到 2,把原下标 2 的 C3003 挪进 tmp;第二次 top=2 > 1,把原下标 1 的 B2002 挪进 tmp;第三次 top=1 > 1 不成立,循环结束。此时 A1001 正好在 slots[top-1] 也就是栈顶,可以弹出。如果把条件错写成top > pos,会把目标车本身也挪进 tmp,目标车就“消失”了;如果写成top >= pos+1,会多挪一次,下标直接越界。这行就是整道题的血泪所在。

为什么辅助结构必须用栈而不是数组或队列?因为 tmp 的“后进先出”特性保证了还原顺序。B2002 和 C3003 被挪出时,C 先进 tmp、B 后进 tmp;而放回时先弹出 B、再弹出 C,恰好恢复原来的“B 在下、C 在上”的相对顺序。这就是两栈换位保序的核心思想,也是答辩时最值得展开讲的一段。

还有一个细节:tmp 是函数内局部变量,每次离场都重新初始化为空栈。它的容量只要不小于 MAX_PARK-1 就够用,因为最多挪走场内除目标车以外的所有车。ParkStack 的数组大小是 MAX_PARK,不存在装不下的问题。

3.3 便道补位与两种题设的差异

leavePark 的末尾做了便道补位:目标车离场后,如果便道非空,就把队头车辆 dequeue 出来停进刚空出的车位。补位的时机必须是“目标车栈内弹出”之后,不能放在打印离场信息之前,否则车位还没腾出来。

补位那一刻有一个计费口径要提前想清楚:补位车的 arriveMin 是保留它最初到达门口的时刻,还是更新成当前入场时刻?我在代码里写的是next.arriveMin = leaveMin,也就是排队等待的时间不计费,从真正停进车位才开始算钱。这是现实停车场最常见的规则。如果你的题面要求“从到达门口开始计费”,把这一行注释掉即可,计费就会从最初到达时刻算起。建议在课设报告里明确写一句“本程序采用入场计费、排队不计费的规则”,让老师知道你不是漏了排队时间。

关于便道车辆的离场,不同题面有分歧。严格版题面认为便道上的车没有进出权限,只能等有车位时自动入场;宽松版允许等不及的车直接从便道开走。我的 leavePark 里,如果目标车不在场内,会先尝试从便道队列中删除它,这就是“便道直接离开”的扩展。如果你们题面不允许这种操作,把removeFromQueue那一段改成报错即可。

int removeFromQueue(WaitQueue *queue, char *plate) { WaitNode **pp = &queue->front; while (*pp) { if (strcmp((*pp)->car.plate, plate) == 0) { WaitNode *dead = *pp; *pp = dead->next; if (queue->rear == dead) { queue->rear = NULL; } free(dead); queue->count--; return 1; } pp = &(*pp)->next; } return 0; }

这段删除用二级指针pp指向“前一个节点的 next 字段”,这样不需要单独维护 prev 节点。删除队尾时,queue->rear还指着被释放的节点,所以要先判断queue->rear == dead并置空,否则后续入队会访问野指针。答辩时如果老师让你手写单链表删除,能写出这个版本会是个加分点。

4. 让它跑起来:事件解析、计费与状态输出

4.1 用 fgets + sscanf 解析 arrive/leave 事件

数据结构课设的程序多半用文本交互,我推荐的事件格式是每行一个事件:arrive 车牌 时刻表示车辆到达门口,leave 车牌 时刻表示车辆请求离场。时刻统一写成 HH:MM,不跨天。解析用 fgets 读整行再 sscanf 提取字段,比直接 scanf 稳得多。

int parseMinute(const char *timeStr) { int h, m; if (sscanf(timeStr, "%d:%d", &h, &m) != 2) { return -1; } if (h < 0 || h > 23 || m < 0 || m > 59) { return -1; } return h * 60 + m; } int main(void) { ParkStack park; WaitQueue queue; initPark(&park); initQueue(&queue); char line[80]; while (fgets(line, sizeof(line), stdin)) { char cmd[8], plate[PLATE_LEN], timeStr[8]; if (sscanf(line, "%7s %15s %7s", cmd, plate, timeStr) != 3) { printf("[错误] 无法解析该行:%s", line); continue; } int t = parseMinute(timeStr); if (t < 0) { printf("[错误] 时间格式应为HH:MM:%s\n", timeStr); continue; } if (strcmp(cmd, "arrive") == 0) { Car car; snprintf(car.plate, PLATE_LEN, "%s", plate); car.arriveMin = t; enterPark(&park, &queue, car); } else if (strcmp(cmd, "leave") == 0) { leavePark(&park, &queue, plate, t); } else { printf("[错误] 未知命令:%s\n", cmd); continue; } printState(&park, &queue); } destroyQueue(&queue); return 0; }

fgets 按行读取能避免一个经典问题:scanf 的%s遇到空格会截断,而且不会消费行尾换行,下一次读入可能拿到空串;换成 fgets 后,每一行是不是完整事件一目了然。sscanf 的返回值必须等于 3,否则说明这行缺字段,直接报错跳过,程序不会崩。格式串里%7s、%15s限制了读入宽度,车牌最多读 15 个字符,剩下的空间留个字符串结束符,这是防缓冲区溢出的基本功,也是代码审查时老师爱看的点。

时间解析返回的是分钟数,08:30 会被解析成 510。这样 4.2 节的计费就只是两个整数相减,不用处理“09:05 减去 08:50”这种字符串借位问题。主循环里每个事件处理完都会调用 printState 打印当前状态,保证每一步都有输出,方便答辩时对着屏幕讲流程。

4.2 计费函数与状态输出

计费规则我做成宏定义:PRICE_PER_HOUR表示每小时收费多少元,默认 5。费用按向上取整的小时数计算:停车 45 分钟按 1 小时收,1 小时零 1 分钟按 2 小时收。

#define PRICE_PER_HOUR 5 void printState(ParkStack *park, WaitQueue *queue) { printf(" 场内[%d/%d] ", park->top, MAX_PARK); for (int i = 0; i < park->top; i++) { printf("| 车位%d:%s ", i + 1, park->slots[i].plate); } printf("| 便道[%d]", queue->count); WaitNode *p = queue->front; while (p) { printf(" %s", p->car.plate); p = p->next; } printf("\n"); } void destroyQueue(WaitQueue *queue) { WaitNode *p = queue->front; while (p) { WaitNode *next = p->next; free(p); p = next; } queue->front = queue->rear = NULL; queue->count = 0; }

计费公式((mins + 59) / 60) * PRICE_PER_HOUR是向上取整的标准写法,少一分钱都多收一小时;如果想按“不足半小时按半小时”计费,把 59 改成 29、除数 60 改成 30,但单价也要相应折半,别只改一半。printState 把栈内车辆从下标 0 到 top-1 按顺序打印,并用“|”分隔车位,这样后进先出的层叠关系在输出里一眼可见;便道队列从 front 向 rear 打印,正好是等待的先后顺序。destroyQueue 在程序结束前遍历释放所有队列节点,valgrind 检查内存泄漏时不会报错。

4.3 一组完整测试样例与预期关键输出

我拿来验收程序的测试样例长这样,容量按 MAX_PARK=3 跑:

arrive A1001 08:30 arrive B2002 08:35 arrive C3003 08:40 arrive D4004 09:00 leave B2002 09:20 arrive E5005 09:30 leave A1001 11:00 leave C3003 11:05 leave E5005 11:30 leave D4004 11:35

这份用例覆盖了四种关键行为:前三次 arrive 直接把车位停满;D4004 到达时触发便道排队;B2002 离场时 C3003 倒库一次、D4004 补位;后续 A1001 离场时发生最深层车辆的倒库。对应输出关键部分应该是:

[入场] A1001 08:30 停入车位1 [入场] B2002 08:35 停入车位2 [入场] C3003 08:40 停入车位3 [排队] D4004 09:00 场内已满,进入便道 [离场] B2002 09:20 停车45分钟,费用5元 [补位] D4004 从便道进入车位3 [入场] E5005 09:30 场内已满,进入便道

我把这些事件按行为整理成了一张表,放在课设报告里能快速说明程序的正确性:

事件预期行为
arrive A1001 08:30场内空,直接停入车位1
arrive D4004 09:00场内已满,D4004 进入便道队尾
leave B2002 09:20倒库 C3003,B2002 离场,D4004 补位
arrive E5005 09:30场内满,E5005 进入便道
leave A1001 11:00倒库 D4004、C3003,A1001 离场,E5005 补位

如果要验证“便道直接离开”分支,单独跑下面这个最小样例就够了:arrive 三辆车停满,第四辆到达后进入便道,然后用 leave 命令点名让第四辆离场。此时它不在场内,removeFromQueue 会从便道删除它,并打印“正在便道排队,直接离开,不产生停车费”。

5. 停车场程序高频避坑:5 个让课设翻车的细节

5.1 top 指针语义漂移:最后一个车位永远空着

现象:容量设成 3,结果场内永远只显示 2 辆车;或者第三辆车刚停进去,下一辆车到达又把它覆盖了。原因:代码里有的地方把 top 当元素个数,有的地方把它当最后一个元素的下标,写倒库循环时必然对不上下标。解决:全程序统一使用“top 是下一个空位下标”,并用三行代码做自检:

if (park->top == MAX_PARK) { /* 满 */ } park->slots[park->top++] = car; /* 入栈 */ Car c = park->slots[--park->top]; /* 出栈 */

这三行就是整套停车场程序的“元规则”,倒库、判满、补位全部从它推导。写完第一版先把这三行贴到代码注释里,改哪里都对着它看,能避免一大半下标错误。

5.2 倒库循环边界写错:要走的车“消失”了

现象:目标车不在场内却出现在输出错误列表里,或者倒库之后场内多了一辆没见过的车。原因:倒库循环条件写成了while (park->top > pos),把目标车自己也挪进了临时栈;写成了while (park->top > pos + 2),则目标车上方离它最近的一辆车没挪干净,倒回去时顺序错位。解决:边界条件固定为while (park->top > pos + 1),并且在写循环前用一张小图手动模拟一遍:场内三辆车 ABC,B 要出场,把 C 挪到临时栈,B 弹出,C 再回来。纸上走通再写代码,这题就不算难题了。

5.3 补位时机遗漏:车位空着,便道却纹丝不动

现象:一辆车离场后,场内明明有空位,便道第一辆车却一直等在原地,直到下一辆车到达才触发入场。原因:leavePark 只处理了弹出和倒库,没有在函数末尾检查便道队列;或者补位逻辑写进了单独的函数但忘了调用。解决:把补位代码直接放在 leavePark 的目标车弹出之后,判断queue->count > 0就 dequeue 队首。另外注意,便道直接离开的事件不能触发补位,因为它本来就不占车位。我见过有人把这两种情况混在一起,结果便道直接离开后场内依然补进一辆车,逻辑就乱了。

5.4 输入解析被换行和长车牌号坑掉

现象:测试文件明明每一行都正确,程序读到第三行开始报“无法解析”;或者输入一个超长车牌号后,后面的时刻字段变成乱码。原因:scanf 系列函数处理%s时遇到空格就断,行尾换行还会残留在缓冲区里影响下一次读取;车牌号没有限制宽度时,超长字符串直接写穿字符数组。解决:统一用 fgets 读行,再用 sscanf 解析,格式串里写死宽度%7s %15s %7s。车牌号本身建议只允许字母、数字和下划线,不要在样例里加入空格,否则任何解析方案都会头疼。

5.5 计费跨天变成负数:时刻基准要统一

现象:样例里有一辆车 23:50 进场、次日 00:30 离场,计费结果却是负数,或者吐出 300 多分钟的诡异时长。原因:parseMinute 把时刻解析成“当天从 0 点开始的分钟数”,跨天车辆的离场分钟数小于入场分钟数,直接相减就是负的。解决:要么约定测试数据不跨天,要么给解析函数加一个日期参数,把日期也换算成全局分钟:

int parseGlobalMinute(int day, const char *timeStr) { return day * 1440 + parseMinute(timeStr); }

这样第二天 00:30 会被解析成 1440+30=1470 分钟,第一天 23:50 是 1430 分钟,差值 40 分钟,计费正确。课设的测试数据一般不会跨天,但如果你要处理真实数据,这行函数就是后悔药。程序里对mins < 0的情况也要打印警告,宁可暴露问题也不要静默算错。

最后补一个调试习惯:把输入样例存成 test.txt,每次改动代码后重新运行并输出到 out.txt,然后用 diff 对比上一次的结果。栈和队列的状态打印行已经足够详细,出问题时看第一处状态和预期不吻合的地方,基本就是定位点。

6. 再往前走一步:动态容量、多区域与验收技巧

6.1 把固定容量改成运行时指定

课设版本用#define MAX_PARK 3写死容量是最省事的,但如果题面要求“容量由用户输入”,就需要把容量从宏变成结构体字段。改造思路很简单:ParkStack 里加一个 cap 成员,slots 改为动态数组,创建函数接收容量参数:

typedef struct { Car *slots; int cap; int top; } ParkStack; ParkStack *createPark(int cap) { ParkStack *p = (ParkStack *)malloc(sizeof(ParkStack)); p->slots = (Car *)malloc(sizeof(Car) * cap); p->cap = cap; p->top = 0; return p; }

主函数里用argc和argv[1]接收容量,没有传参时默认 3,所有引用 MAX_PARK 的地方替换成 park->cap 即可。要注意 enterPark 的判满条件改成park->top < park->cap,printState 打印容量时也要用 park->cap,不要残留宏名。

6.2 从单栈到多区域:让栈变成栈数组

同样一套倒库算法,把单个停车场换成多个并列区域,就是一个很自然的进阶题。设计是定义一个停车场管理系统,内部持有多个 ParkStack 区域。车辆到达时遍历所有区域,找第一个没有满员的栈停入;离场时先遍历确认车在哪个区域,再对该区域执行 leavePark 逻辑。便道仍然是一个全局队列,补位时优先补到最早空出的区域。这样改动的核心是把“栈”扩展成“栈的数组”,代码量不会暴增,但能在报告里多写一节设计说明,答辩时可讲的内容更丰富。

6.3 让老师一眼看懂的验收技巧

程序跑通只是第一步,课设分数还取决于老师能不能快速确认“你确实实现了栈和队列”。我自己的验收流程是三步:第一步,用最简样例验证链队列不会把管理系统的内存留坑:编译时加-Wall -Wextra,跑完用 valgrind 查一次,队列节点全释放就是干净代码。

gcc -Wall -Wextra -O2 -o park parking_lot.c ./park < test_cases.txt > out.txt cat out.txt

第二步,把 test.txt 里的事件设置成能够覆盖“入栈、出栈、倒库、补位、队列直接离开”五类行为,每次改代码后 diff 输出,确认没有任何一行状态变化超出预期。第三步,把关键输出截图放进课设报告,每张截图下面写清楚这一行对应题目里的哪条规则。

我第一次写这份作业时,就在 top 指针上栽了跟头,调了一整晚才发现判满条件写成了top == MAX_PARK - 1。后来养成一个习惯:任何涉及栈和队列下标的循环,先在草稿纸上画出容器状态,再写代码。这道题真正的收获不是“会写停车场”,而是以后遇到任何后进先出、先进先出的场景,都能条件反射地想到该用哪种结构去建模。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询