数据结构——队列(银行叫号系统)
一、实验目的
(1)掌握队列的链式存储结构
(2)掌握队列的基本操作,并能进行应用实践
(3)使用C/C++语言和队列实现”银行叫号系统“专题
二、实验任务
设计一个控制台程序,模拟银行排队业务,要求实现功能:
(1)输出程序界面,提示客户输入银行窗口数量和开放窗口时间
(2)客户随机时间进入银行,程序在事件表中记录客户进入时间和离开时间,并将客户排入窗口队列,办理完业务后退出队列
(3)窗口开放时间结束,客户停止进入银行,直到所有客户退出队列之后计算出客户的平均服务时间
三、实验代码
1.事件列表
1)定义事件结点
typedef struct Event//定义事件结点
{
int occurTime;//发生时刻
int type;//事件类型:0表示顾客到达;1~N表示顾客从N号窗口离开
struct Event *next;
}Event,*EventList;
2)初始化事件链表
int InitList(EventList* pList)//初始化事件链表
{
*pList=(Event*)malloc(sizeof(Event));
if(*pList==NULL)
{
printf("分配内存失败\n");
exit(-1);
}
(*pList)->next=NULL;
return OK;
}
3)插入元素
int OrderInsert(EventList pList,Event sEvent)//插入元素
{
Event *pAfter,*pBefore;//临时变量,用于在链表中插入结点
pAfter=pList;
pBefore=pList->next;
while(pAfter!=NULL && sEvent.occurTime>pAfter->occurTime)//比较事件发生的时间
{
pBefore=pAfter;
pAfter=pAfter->next;
}
pBefore->next=(Event*)malloc(sizeof(Event));//创建一个新的结点,挂在Before和After两个结点中间
pBefore->next->occurTime=sEvent.occurTime;//对新结点赋值
pBefore->next->type=sEvent.type;
pBefore->next->next=pAfter;
return OK;
}
4)判断链表是否为空
int EmptyList(EventList pList)//判断链表是否为空
{
if(pList->next==NULL)
return TRUE;
else
return FALSE;
}
5)删除首结点
int DelFirst(EventList pList,Event *pEvent)//删除首结点
{
Event *pTmp;
if(EmptyList(pList))
{
printf("链表为空");
return ERROR;
}
else
{
pTmp=pList->next;//删除链表首结点
pList->next=pTmp->next;
*pEvent=*pTmp;//保存数值
free(pTmp);//释放内存
return OK;
}
}
6)遍历链表
int ListTraverse(EventList pList)//遍历链表
{
Event *pTmp;
pTmp=pList;
while(pTmp->next!=NULL)
{
pTmp=pTmp->next;
if(pTmp->type==0)
printf("第%d分钟,下一名客户即将到来。\n",pTmp->occurTime);
else
printf("第%d分钟,%d号窗口的客户即将离开。\n",pTmp->occurTime,pTmp->type);
}
printf("\n");
return OK;
}
2.链式队列
1)定义链式队列数据结构
typedef struct QElemType{//定义链式队列数据结构
int arriveTime;//到达时间
int duration;//持续时间
struct QElemType *next;
}QElemType;
typedef struct LinkedQueue{
QElemType *front;//头指针
QElemType *rear;//尾指针
}LinkedQueue;
2)初始化队列
int InitQueue(LinkedQueue *pQueue)//初始化队列
{
pQueue->front=pQueue->rear=(QElemType*)malloc(sizeof(QElemType));//分配内存
if(pQueue->front==NULL)
{
printf("分配内存失败\n");
exit(-1);
}
pQueue->front->next=NULL;
return OK;
}
3)判断链表是否为空
int EmptyQueue(LinkedQueue *pQueue)//判断链表是否为空
{
if(pQueue->front==pQueue->rear)
return TRUE;
else
return FALSE;
}
4)首结点出队
int DelQueue(LinkedQueue *pQueue,QElemType *pQElem)//首结点出队
{
QElemType *pTmp;//临时结点指针
if(EmptyQueue(pQueue))
{
printf("队列为空,不能继续出队列\n");
return ERROR;
}
else
{//指向首结点后一个元素,并复制给pQElem
pTmp=pQueue->front->next;
*pQElem=*pTmp;
pQueue->front->next=pTmp->next;//删除这个结点
if(pQueue->rear==pTmp)
pQueue->rear=pQueue->front;
free(pTmp);
return OK;
}
}
5)结点入队
int EnQueue(LinkedQueue *pQueue,QElemType sQElem)//结点入队
{
QElemType *pTmp;//临时结点指针
pTmp=(QElemType*)malloc(sizeof(QElemType));
if(pTmp==NULL)
{
printf("内存分配失败\n");
exit(-1);
}
else
{//尾结点指向新入队的元素
*pTmp=sQElem;
pTmp->next=NULL;
pQueue->rear->next=pTmp;
pQueue->rear=pTmp;
}
return OK;
}
6)获取队列长度
int QueueLength(LinkedQueue *pQueue)//获取队列长度
{
QElemType *pTmp;//临时结点指针
int count=0;//遍历链表,统计结点数
pTmp=pQueue->front->next;//队列第一个结点
while(pTmp!=NULL)
{
count++;
pTmp=pTmp->next;
}
return count;
}
7)获取队列首结点
int GetHead(LinkedQueue *pQueue,QElemType *pQElem)//获取队列首结点
{
if(EmptyQueue(pQueue))
{
printf("队列为空");
return ERROR;
}
*pQElem=*(pQueue->front->next);
return OK;
}
8)遍历队列
int QueueTraverse(LinkedQueue *pQueue)//遍历队列
{
QElemType *pTmp;//临时结点指针
if(EmptyQueue(pQueue))
{
printf("队列为空\n");
return ERROR;
}
pTmp=pQueue->front->next;//队列第一个结点
while(pTmp!=NULL)
{
printf(">[到达时刻:第%d分钟,服务时长:%d分钟]\n",pTmp->arriveTime,pTmp->duration);
pTmp=pTmp->next;
}
printf("\n");
return OK;
}
3.主要业务功能
1)定义全局变量
#define MAXSIZE 20//银行服务窗口最大数量
int gWindowsNum;//银行服务窗口数
int gCustomerNum;//客户总人数
int gTotalTime;//总服务时间
int gCloseTime;//银行关闭时间
EventList gEventList;//事件列表
Event gEvent;//事件
LinkedQueue gQueue[MAXSIZE];//队列数组
QElemType gCustomer;//队列结点
2)初始化数据
void Initialize( )//初始化数据
{
int i;
gTotalTime=0;
gCustomerNum=0;
InitList(&gEventList);//初始化事件列表
printf("请输入银行服务窗口个数(1~20):");//服务窗口个数
scanf("%d",&gWindowsNum);
while (gWindowsNum<1 || gWindowsNum>MAXSIZE)
{
printf("请输入1到%d之间的整数:",MAXSIZE);
scanf("%d",&gWindowsNum);
}
printf("\n请输入服务关闭时间(超过这个时间就不在接纳新顾客)(单位:分钟):");//服务关闭时间
scanf("%d",&gCloseTime);
while(gCloseTime<1)
{
printf("请输入大于零的整数:");
scanf("%d",&gCloseTime);
}
for(i=0;i<gWindowsNum;i++)//为每个窗口建立一个空队列
{
InitQueue(&gQueue[i]);
}
}
3)处理客户到达事件
int ShortestQueue( );
void CustomerArrived()//处理客户到达事件
{
QElemType sQElem;
Event sEvent;
int index;//排队人数最少的窗口编号
int arrivetime;//客户到达时间
int duration;//业务办理时间
printf("当前时刻:第%d分钟\n", gEvent.occurTime);//顾客到达的时间,在上一位顾客之后1~5分钟
arrivetime = gEvent.occurTime+ rand() % 5 + 1;
duration = rand() % 21 + 10;//办理业务时间为10~30分钟
if (arrivetime < gCloseTime)//服务尚未关闭
{
gCustomerNum++;//新顾客到达事件
sEvent.occurTime = arrivetime;
sEvent.type = 0;
OrderInsert(gEventList, sEvent);//顾客进入人数最少的窗口排队
sQElem.arriveTime = gEvent.occurTime;//入队时刻
sQElem.duration = duration;//办理业务时间
index = ShortestQueue();
EnQueue(&gQueue[index], sQElem);//入列
//如果恰好排在了队首,预定离开事件
if (QueueLength(&gQueue[index]) == 1)
{
//记录顾客从第index+1号号窗口离开(因为索引从0开始)
sEvent.occurTime =gEvent.occurTime + duration;
sEvent.type = index + 1;
OrderInsert(gEventList, sEvent);
}
}
else//银行排队服务关闭,不再接受新客户
printf("\n排队服务以关闭,不再接受新客户!\n");
}
4)处理顾客离开事件
void CustomerLeaved( )//处理顾客离开事件
{
Event sEvent;
int index=gEvent.type-1;//队列编号为窗口编号-1
DelQueue(&gQueue[index],&gCustomer);//删除队首结点
printf("\n顾客离开时间:第%d分钟。",gEvent.occurTime);
gTotalTime+=gCustomer.duration;//记录服务时间
//如果队列不为空,则预定下一位顾客从第index+1号窗口离开
if(!EmptyQueue(&gQueue[index]))
{
GetHead(&gQueue[index],&gCustomer);//获得下一位顾客
//记录离开事件
sEvent.occurTime=gEvent.occurTime+gCustomer.duration;
sEvent.type=index+1;
OrderInsert(gEventList,sEvent);
}
}
5)获取最短队列的编号
int ShortestQueue( )//获取最短队列的编号
{
int i=0;
int min=9999;//最短队列的长度
int index=-1;//最短队列的编号
int length=0;
//遍历各个窗口,比较哪个窗口排队的人最少
for(i=0;i<gWindowsNum;i++)
{
length=QueueLength(&gQueue[i]);
if(min>length)
{
min=length;
index=i;
}
}
return index;
}
6)显示当前窗口队列
void PrintQueue( )//显示当前窗口队列
{
int i;
printf("\n窗口排队状态:\n");
for(i=0;i<gWindowsNum;i++)
{
printf("%d号窗口:\n",i+1);
QueueTraverse(&gQueue[i]);
}
printf("\n");
}
7)显示当前时间表
void PrintEventList( )//显示当前时间表
{
printf("\n事件表状态:\n");
ListTraverse(gEventList);
}
8)银行排队模拟
void BankSimulation( )//银行排队模拟
{
//随机数发生器,用于模拟随机客户排队事件
//根据当前系统时间初始化随机种子
srand( (unsigned)time(NULL));
//准备开业
Initialize( );
//第一个顾客到来
gEvent.occurTime=0;
gEvent.type=0;
OrderInsert(gEventList,gEvent);
//处理排队列表
while(!EmptyList(gEventList))
{
DelFirst(gEventList,&gEvent);
//处理顾客事件
if(gEvent.type==0)
CustomerArrived( );
else
CustomerLeaved( );
//显示当前事件列表,以及排队情况
PrintEventList( );
PrintQueue( );
//暂停一会,便于观察输出内容
system("PAUSE");
printf("\n");
}
//平均服务时间
printf("\n");
printf("客户平均服务时间:%f分钟\n",(float)gTotalTime/gCustomerNum);
system("PAUSE");
}
4.主函数入口
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<time.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -2
int main( )//主函数入口
{
BankSimulation( );
return 0;
}
5.程序运行截图