一 题目内容
停车场管理系统
设停车场内只有一个可停放n辆汽车的狭长通道,且只有一个大门可供汽车进出。汽车在停车场内按车辆到达时间的先后顺序,依次由南向北排列(大门在最北端,最先到达的第一辆车停放在车场的最南端),若车场内已停满n辆汽车,则后来的汽车只能在门外的便道即候车场上等候,一旦有车开走,则排在便道上的第一辆车即可开入;当停车场内某辆车要离开时,在它之后开入的车辆必须先退出车场为它让路,待该辆车开出大门外,其它车辆再按原次序进入车场,每辆停放在车场的车在它离开停车场时必须按它停留的时间长短交纳费用。
用栈模拟停车场,用队列模拟车场外的便道,按照从键盘获取的输入数据序列进行模拟管理。每一组输入数据包括3个数据项:汽车“到达”或“离开”信息、汽车牌照号码及到达或离开的时刻,对每一组输入数据进行操作后的输出信息为:若是车辆到达,则输出汽车在停车场内或便道上的停车位置;若是车辆离开;则输出汽车在停车场内停留的时间和应交纳的费用(在便道上停留的时间不收费)。这里栈以顺序存储结构实现,队列以循环队列实现。
另外,还需设一个临时栈,用于临时停放为要给离开的汽车让路而从停车场退出来的汽车,也用顺序结构实现。试为停车场编制按上述要求进行管理的模拟程序。
二 涉及知识点的补充
1, 栈(stack)
- 栈是一种遵循先入后出的逻辑的线性数据结构。你可以把它的结构看做一个弹夹,子弹从弹夹上方压入弹夹(入栈/压栈),也只能从弹夹上方取出来(出栈)。
更多的细节和基础操作的实现可自行学习。
栈:链栈和顺序栈的实现https://blog.csdn.net/Mzyh_c/article/details/135180651?spm=1001.2014.3001.5501
2, 队列(queue)
- 队列是一种遵循先入先出规则的线性数据结构。结构和排队类似,队尾不断有新的人加入(入队),而队头不断有人离开(出队)。
更多的细节和基础操作的实现可自行学习。
队列:栈队和顺序队的实现https://blog.csdn.net/Mzyh_c/article/details/135187848?spm=1001.2014.3001.5501
三 实现过程
为了代码的可读性,所有代码分为了一个头文件head.h和一个源文件Carpark.c
1,头文件head.h的代码部分
(1) 头文件的包含,宏定义常量
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<stdlib.h>
#include<stdio.h>
#define MAXSIZE_St 3 //栈最大容量
#define MAXSIZE_Qu 10 //队列最大容量
#define Parking_price 5 //停车费5/h
(2)定义结构体,声明函数
/* 定义结构体 */
//车辆信息
typedef struct Car
{
int CarNo; //车牌号
int CarTime;//车进入时间
}Car;
//顺序栈(停车场和)
typedef struct SqStack
{
Car* data;//记录车辆信息
int top; //栈顶
int size; //长度
}SqStack;
//循环队列(候车场)
typedef struct SqQueue
{
Car* data;//记录车辆信息
int top; //队头
int end; //队尾
int size; //长度
}SqQueue;
/* 栈(停车场)操作函数: */
//初始化栈,为停车场分配空间
SqStack* InitStack();
//StackEmpty:检查栈是否为空,即停车场是否无车。
int StackEmpty(SqStack* s);
//StackFull:检查栈是否已满,即停车场是否已满。
int StackFull(SqStack* s);
//StackLength:计算栈的长度,即停车场内车辆数量。
int StackLength(SqStack* s);
//Push:将新车辆加入到栈(停车场)中。
void Push(SqStack* s, int n, int t1);
//Pop:从栈中移除车辆。
void Pop(SqStack* s);
//DispStack:显示栈中的所有车辆信息。
void DispStack(SqStack* s);
/* 队列(候车场)操作函数: */
//初始化队列,为候车场分配空间
SqQueue* InitQueue();
//QueueEmpty:检查队列是否为空,即候车场是否无车。
int QueueEmpty(SqQueue* q);
//QueueFull:检查队列是否已满,即候车场是否已满。
int QueueFull(SqQueue* q);
//QueueLength:计算队列的长度,即候车场内车辆数量。
int QueueLength(SqQueue* q);
//EnQueue:将新车辆加入到队列(候车场)中。
void EnQueue(SqQueue* q, int n, int t1);
//DeQueue:从队列中移除车辆。
void DeQueue(SqQueue* q);
//DispQueue:显示队列中的所有车辆信息。
void DispQueue(SqQueue* q);
/* 用户操作对应函数: */
//Parking_Arrive: 停车场有车到达
void Parking_Arrive(SqStack* s1, SqQueue* q);
//Parking_Leave: 停车场有车离开
void Parking_Leave(SqStack* s1, SqStack* s2, SqQueue* q);
2,源文件Carpark.c的代码部分
记得在源文件开头包含头文件 #include"head.h"
(1)main函数的实现
int main()
{
//初始化两个停车场栈(S1和S2)和一个候车场队列(Q)
SqStack* S1 = InitStack();
SqStack* S2 = InitStack();
SqQueue* Q = InitQueue();
int select = 0; //用户输入的操作指令
do {
printf(">请输入指令(1:到达 2:离开 3:停车场 4:候车场 0:退出): ");
scanf("%d", &select);
switch (select)
{
case 1:
Parking_Arrive(S1,Q);
break;
case 2:
Parking_Leave(S1,S2,Q);
break;
case 3:
DispStack(S1);
break;
case 4:
DispQueue(Q);
break;
case 0:
printf("已退出Carpark.c!\n");
break;
default:
printf("请重新输入\n");
getchar();
break;
}
} while (select);
return 0;
}
main函数中先调用了初始化栈和队列的函数创建了停车场(栈)和候车场(队列)的变量,然后在do{ }while( )循环中根据用户键盘所输入指针执行对应的函数对栈和队列进行操作。
(2)停车场(栈)基于数组的实现
基本的函数实现和顺序栈的操作差不多,只有“打印停车场”函数需要额外补充。
//初始化栈,为停车场分配空间
SqStack* InitStack()
{
SqStack* s = (SqStack*)malloc(sizeof(SqStack));
s->data = (Car*)malloc(sizeof(Car) * MAXSIZE_St);
if (s->data == NULL || s->data == NULL)
{
return NULL;//空间开辟失败
}
//本代码中size和top这2个没什么区别(可简化为一个)
s->size = 0;
s->top = 0;//表示栈为空
//返回开辟好的栈空间
return s;
}
//检查栈是否为空,即停车场是否无车。
int StackEmpty(SqStack* s)
{
if (s->top == 0)
return 1;//为空
else
return 0;//不为空
}
//检查栈是否已满,即停车场是否已满。
int StackFull(SqStack* s)
{
if (s->size != MAXSIZE_St)
return 0;//未满
else
return 1;//已满
}
//计算栈的长度,即停车场内车辆数量。
int StackLength(SqStack* s)
{
return s->size;
}
//将新车辆加入到栈(停车场)中。
void Push(SqStack* s, int n, int t1)
{
//将形参的数据赋值给结构体成员
s->data[s->top].CarNo = n;
s->data[s->top].CarTime = t1;
//top和size自增,指向下一个空位置
s->top++;
s->size++;
}
//从栈中移除车辆。
void Pop(SqStack* s)
{
if (StackEmpty(s))
{
printf("停车场为空\n");
return;
}
//top和size自减,忽略原来的位置,指向上一个位置
s->top--;
s->size--;
}
//显示栈中的所有车辆信息。
void DispStack(SqStack* s)
{
printf("车牌号\t进场时间\n");
//对栈是否为空进行判断
if (StackEmpty(s))
{
printf("EMPTY...\n");
return;
}
//循环打印停车场(栈S1)中的元素
int i = 0;
//size-1是因为在入栈时size最后会自增指向空位置
for (i = s->size-1; i >= 0; i--)
{
printf("%-4d\t%-2d\n",s->data[i].CarNo, s->data[i].CarTime);
}
}
(3)候车场(队列)基于循环数组的实现
基本的函数实现和循环队列的操作差不多,这里也只需要额外补充“打印候车场”函数。
//初始化队列,为候车场分配空间
SqQueue* InitQueue()
{
SqQueue* q = malloc(sizeof(SqQueue));
q->data = (Car*)malloc(sizeof(Car) * MAXSIZE_Qu);
if (!q || !(q->data))
{
return NULL;//开辟空间失败
}
//初始头指针和尾指针指向0索引
q->top = q->end = 0;
//返回开辟好的队列空间
return q;
}
//QueueEmpty:检查队列是否为空,即候车场是否无车。
int QueueEmpty(SqQueue* q)
{
if (q->top == q->end)
{
return 1;//为空
}
return 0;//不为空
}
//QueueFull:检查队列是否已满,即候车场是否已满。
int QueueFull(SqQueue* q)
{
//剩下一个空位时,入队后end+1刚好等于top
//模(%)最大容量可以防止结果大于最大容量
return ((q->end + 1) % MAXSIZE_Qu == q->top) ? 1 : 0;
//队列满了返回0, 反之返回1.
}
//QueueLength:计算队列的长度,即候车场内车辆数量。
int QueueLength(SqQueue* q)
{
//由于是循环队列, 所以需要加上最大容量,防止出现负数
return (q->end - q->top + MAXSIZE_Qu) % MAXSIZE_Qu;
}
//EnQueue:将新车辆加入到队列(候车场)中。
void EnQueue(SqQueue* q, int n, int t1)
{
if (QueueFull(q))
{
//队列已满,无法添加,返回
return;
}
q->data[q->end].CarNo = n;
q->data[q->end].CarTime = t1;
//入队成功后,尾指针指向下一个位置
q->end = (q->end + 1) % MAXSIZE_Qu;
}
//DeQueue:从队列中移除车辆。
void DeQueue(SqQueue* q)
{
if (QueueEmpty(q))
{
return;//队列为空
}
//出队成功后,头指针指向下一个位置
q->top = (q->top + 1) % MAXSIZE_Qu;
}
//DispQueue:显示队列中的所有车辆信息。
void DispQueue(SqQueue* q)
{
if (QueueEmpty(q))
{
return;//队列为空
}
//循环打印候车场(队列Q)中的元素
int i = 0;
printf("候车场中的车辆: \n");
for (i = 0; i < QueueLength(q); i++)
{
//出队后头指针会向后走,所以头指针决定了元素的索引
printf(">>> %d\n",q->data[i+q->top].CarNo);
}
}
(4)用户操作函数的实现
本块代码用于 车辆到达和离开 功能的实现,利用栈和队列的特性进行代码编写。
部分实现过程可下图:
//Parking_Arrive: 用户操作添加车辆(到达)
void Parking_Arrive(SqStack* s1, SqQueue* q)
{
int n = 0; //用户输入的车号
int t1 = 0; //用户输入的车的到达时间
if (!StackFull(s1))
{
//停车场S1未满时
printf("车号 到达时间: ");
scanf("%d %d", &n, &t1);
//栈S1进行入栈
Push(s1, n, t1);
//打印目前入栈元素所在位置(地址)
Car* p = &(s1->data[s1->size-1]);
printf("所在停车场位置: %#x\n", p);//输出十六进制地址
}
else
{
//停车场S1已满时
printf("车号 到达时间: ");
scanf("%d %d", &n, &t1);
//队列Q进行入队
EnQueue(q, n, t1);
//打印目前入队元素所在位置(索引)
printf("所在候车场位置: %d\n", (q->end)-1);
}
return;
}
//Parking_Leave: 用户操作退出车辆(离开)
void Parking_Leave(SqStack* s1, SqStack* s2, SqQueue* q)
{
int i = 0;
int n = 0; //用户输入的车号
int t2 = 0; //用户输入的车的离开时间
int index = -1; //用于记录对应车号的索引
int count = 0; //记录出栈入栈次数
printf("车号 离开时间: ");
scanf("%d %d", &n, &t2);
//找到要离开的车,记录其索引
for (i = s1->size - 1; i >= 0; i--)
{
if (n == s1->data[i].CarNo)
{
index = i;
break;
}
}
//处理异常情况
if (index == -1)
{
printf("无对应车号的车辆!\n");
return;
}
//说明离开的车所花停车费用
int price = (t2 - s1->data[index].CarTime) * Parking_price;
printf("该汽车的停车费用为: %d.\n", price);
//将需要离开的车其后面的车移动到栈S2中
for (i = s1->size - 1; i > index; i--)
{
//将对应元素入栈到S2中后,再从S1中出栈
Push(s2, s1->data[i].CarNo, s1->data[i].CarTime);
Pop(s1);
//每有一辆车进入S2则count加一
count++;
}
//栈的性质: 先进后出...
//将离开的车(目前处于栈顶)出栈
Pop(s1);
//先将暂存S2中的车入栈到栈S1中(S2有车的情况下)
for (i = count - 1; i >= 0; i--)
{
//S2中的车入栈到S1中,再在S2中进行出栈
Push(s1, s2->data[i].CarNo, s2->data[i].CarTime);
Pop(s2);
}
//如果栈S1未满,则将候车场Q的车入栈到S1中
if (!StackFull(s1) && !QueueEmpty(q))
{
for (i = 0; i < MAXSIZE_St - s1->size; i++)
{
//队列Q中的车入栈到S1中,再在Q中进行出队
Push(s1, q->data[i+q->top].CarNo,
q->data[i+q->top].CarTime);
DeQueue(q);
}
}
return;
}
四 实现效果和全部代码
1,实现效果
2,全部代码
本段代码已经整合,可直接复制使用。
#define _CRT_SECURE_NO_WARNINGS
#include<stdlib.h>
#include<stdio.h>
#define MAXSIZE_St 3 //栈最大容量
#define MAXSIZE_Qu 10 //队列最大容量
#define Parking_price 5 //停车费5/h
//基本结构(结构体)
//车辆信息
typedef struct Car
{
int CarNo; //车牌号
int CarTime;//车进入时间
}Car;
//顺序栈(停车场和)
typedef struct SqStack
{
Car* data;//记录车辆信息
int top; //栈顶
int size; //长度
}SqStack;
//循环队列(候车场)
typedef struct SqQueue
{
Car* data;//记录车辆信息
int top; //队头
int end; //队尾
int size; //长度
}SqQueue;
//函数的声明
//栈(停车场)操作函数:
SqStack* InitStack();
int StackEmpty(SqStack* s);
int StackFull(SqStack* s);
int StackLength(SqStack* s);
void Push(SqStack* s, int n, int t1);
void Pop(SqStack* s);
void DispStack(SqStack* s);
//队列(候车场)操作函数:
SqQueue* InitQueue();
int QueueEmpty(SqQueue* q);
int QueueFull(SqQueue* q);
int QueueLength(SqQueue* q);
void EnQueue(SqQueue* q, int n, int t1);
void DeQueue(SqQueue* q);
void DispQueue(SqQueue* q);
//用户操作对应函数:
void Parking_Arrive(SqStack* s1, SqQueue* q);
void Parking_Leave(SqStack* s1, SqStack* s2, SqQueue* q);
//主函数
int main()
{
//初始化两个停车场栈(S1和S2)和一个候车场队列(Q)
SqStack* S1 = InitStack();
SqStack* S2 = InitStack();
SqQueue* Q = InitQueue();
int select = 0; //用户输入的操作指令
do {
printf(">请输入指令(1:到达 2:离开 3:停车场 4:候车场 0:退出): ");
scanf("%d", &select);
switch (select)
{
case 1:
Parking_Arrive(S1,Q);
break;
case 2:
Parking_Leave(S1,S2,Q);
break;
case 3:
DispStack(S1);
break;
case 4:
DispQueue(Q);
break;
case 0:
printf("已退出Carpark.c!\n");
break;
default:
printf("请重新输入\n");
getchar();
break;
}
} while (select);
return 0;
}
//函数的定义
//初始化栈,为停车场分配空间
SqStack* InitStack()
{
SqStack* s = (SqStack*)malloc(sizeof(SqStack));
s->data = (Car*)malloc(sizeof(Car) * MAXSIZE_St);
if (s->data == NULL || s->data == NULL)
{
return NULL;//空间开辟失败
}
//本代码中size和top这2个没什么区别(可简化为一个)
s->size = 0;
s->top = 0;//表示栈为空
//返回开辟好的栈空间
return s;
}
//检查栈是否为空,即停车场是否无车。
int StackEmpty(SqStack* s)
{
if (s->top == 0)
return 1;//为空
else
return 0;//不为空
}
//检查栈是否已满,即停车场是否已满。
int StackFull(SqStack* s)
{
if (s->size != MAXSIZE_St)
return 0;//未满
else
return 1;//已满
}
//计算栈的长度,即停车场内车辆数量。
int StackLength(SqStack* s)
{
return s->size;
}
//将新车辆加入到栈(停车场)中。
void Push(SqStack* s, int n, int t1)
{
//将形参的数据赋值给结构体成员
s->data[s->top].CarNo = n;
s->data[s->top].CarTime = t1;
//top和size自增,指向下一个空位置
s->top++;
s->size++;
}
//从栈中移除车辆。
void Pop(SqStack* s)
{
if (StackEmpty(s))
{
printf("停车场为空\n");
return;
}
//top和size自减,忽略原来的位置,指向上一个位置
s->top--;
s->size--;
}
//显示栈中的所有车辆信息。
void DispStack(SqStack* s)
{
printf("车牌号\t进场时间\n");
//对栈是否为空进行判断
if (StackEmpty(s))
{
printf("EMPTY...\n");
return;
}
//循环打印停车场(栈S1)中的元素
int i = 0;
//size-1是因为在入栈时size最后会自增指向空位置
for (i = s->size-1; i >= 0; i--)
{
printf("%-4d\t%-2d\n",s->data[i].CarNo, s->data[i].CarTime);
}
}
//初始化队列,为候车场分配空间
SqQueue* InitQueue()
{
SqQueue* q = malloc(sizeof(SqQueue));
q->data = (Car*)malloc(sizeof(Car) * MAXSIZE_Qu);
if (!q || !(q->data))
{
return NULL;//开辟空间失败
}
//初始头指针和尾指针指向0索引
q->top = q->end = 0;
//返回开辟好的队列空间
return q;
}
//QueueEmpty:检查队列是否为空,即候车场是否无车。
int QueueEmpty(SqQueue* q)
{
if (q->top == q->end)
{
return 1;//为空
}
return 0;//不为空
}
//QueueFull:检查队列是否已满,即候车场是否已满。
int QueueFull(SqQueue* q)
{
//剩下一个空位时,入队后end+1刚好等于top
//模(%)最大容量可以防止结果大于最大容量
return ((q->end + 1) % MAXSIZE_Qu == q->top) ? 1 : 0;
//队列满了返回0, 反之返回1.
}
//QueueLength:计算队列的长度,即候车场内车辆数量。
int QueueLength(SqQueue* q)
{
//由于是循环队列, 所以需要加上最大容量,防止出现负数
return (q->end - q->top + MAXSIZE_Qu) % MAXSIZE_Qu;
}
//EnQueue:将新车辆加入到队列(候车场)中。
void EnQueue(SqQueue* q, int n, int t1)
{
if (QueueFull(q))
{
//队列已满,无法添加,返回
return;
}
q->data[q->end].CarNo = n;
q->data[q->end].CarTime = t1;
//入队成功后,尾指针指向下一个位置
q->end = (q->end + 1) % MAXSIZE_Qu;
}
//DeQueue:从队列中移除车辆。
void DeQueue(SqQueue* q)
{
if (QueueEmpty(q))
{
return;//队列为空
}
//出队成功后,头指针指向下一个位置
q->top = (q->top + 1) % MAXSIZE_Qu;
}
//DispQueue:显示队列中的所有车辆信息。
void DispQueue(SqQueue* q)
{
if (QueueEmpty(q))
{
return;//队列为空
}
//循环打印候车场(队列Q)中的元素
int i = 0;
printf("候车场中的车辆: \n");
for (i = 0; i < QueueLength(q); i++)
{
//出队后头指针会向后走,所以头指针决定了元素的索引
printf(">>> %d\n",q->data[i+q->top].CarNo);
}
}
//Parking_Arrive: 用户操作添加车辆(到达)
void Parking_Arrive(SqStack* s1, SqQueue* q)
{
int n = 0; //用户输入的车号
int t1 = 0; //用户输入的车的到达时间
if (!StackFull(s1))
{
//停车场S1未满时
printf("车号 到达时间: ");
scanf("%d %d", &n, &t1);
//栈S1进行入栈
Push(s1, n, t1);
//打印目前入栈元素所在位置(地址)
Car* p = &(s1->data[s1->size-1]);
printf("所在停车场位置: %#x\n", p);//输出十六进制地址
}
else
{
//停车场S1已满时
printf("车号 到达时间: ");
scanf("%d %d", &n, &t1);
//队列Q进行入队
EnQueue(q, n, t1);
//打印目前入队元素所在位置(索引)
printf("所在候车场位置: %d\n", (q->end)-1);
}
return;
}
//Parking_Leave: 用户操作退出车辆(离开)
void Parking_Leave(SqStack* s1, SqStack* s2, SqQueue* q)
{
int i = 0;
int n = 0; //用户输入的车号
int t2 = 0; //用户输入的车的离开时间
int index = -1; //用于记录对应车号的索引
int count = 0; //记录出栈入栈次数
printf("车号 离开时间: ");
scanf("%d %d", &n, &t2);
//找到要离开的车,记录其索引
for (i = s1->size - 1; i >= 0; i--)
{
if (n == s1->data[i].CarNo)
{
index = i;
break;
}
}
//处理异常情况
if (index == -1)
{
printf("无对应车号的车辆!\n");
return;
}
//说明离开的车所花停车费用
int price = (t2 - s1->data[index].CarTime) * Parking_price;
printf("该汽车的停车费用为: %d.\n", price);
//将需要离开的车其后面的车移动到栈S2中
for (i = s1->size - 1; i > index; i--)
{
//将对应元素入栈到S2中后,再从S1中出栈
Push(s2, s1->data[i].CarNo, s1->data[i].CarTime);
Pop(s1);
//每有一辆车进入S2则count加一
count++;
}
//栈的性质: 先进后出...
//将离开的车(目前处于栈顶)出栈
Pop(s1);
//先将暂存S2中的车入栈到栈S1中(S2有车的情况下)
for (i = count - 1; i >= 0; i--)
{
//S2中的车入栈到S1中,再在S2中进行出栈
Push(s1, s2->data[i].CarNo, s2->data[i].CarTime);
Pop(s2);
}
//如果栈S1未满,则将候车场Q的车入栈到S1中
if (!StackFull(s1) && !QueueEmpty(q))
{
for (i = 0; i < MAXSIZE_St - s1->size; i++)
{
//队列Q中的车入栈到S1中,再在Q中进行出队
Push(s1, q->data[i+q->top].CarNo, q->data[i+q->top].CarTime);
DeQueue(q);
}
}
return;
}
本篇文章现在没什么额外的详细解析,基本都写在代码注释里。
若有疑问和错误请在评论区留言。 ---2023.12.24