1. 背景说明
该模拟业务基于时间线来确定,当事件发生时,通过插入升序链表来模拟时间线记录事件发生时间、类型,类似于记事本,由于
时间是单向的,正好符合队列的先进先出特性,类似于我们生活中的排队行为。当记录客户离开某个队列时,离开的只能是该队
列队头的客户(不存在插队行为),因此使用队列的好处在于无需考虑按照时间线离开时离开的具体是哪个客户。
2. 示例代码
1) status.h
/* DataStructure 预定义常量和类型头文件 */
#ifndef STATUS_H
#define STATUS_H
#define CHECK_NULL(pointer) if (!(pointer)) { \
printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_NULL_PTR); \
return NULL; \
}
#define CHECK_RET(ret) if (ret != RET_OK) { \
printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ret); \
return ret; \
}
#define CHECK_VALUE(value, ERR_CODE) if (value) { \
printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_CODE); \
return ERR_CODE; \
}
#define CHECK_FALSE(value, ERR_CODE) if (!(value)) { \
printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_CODE); \
return FALSE; \
}
/* 函数结果状态码 */
#define TRUE 1 /* 返回值为真 */
#define FALSE 0 /* 返回值为假 */
#define RET_OK 0 /* 返回值正确 */
#define INFEASIABLE 2 /* 返回值未知 */
#define ERR_MEMORY 3 /* 访问内存错 */
#define ERR_NULL_PTR 4 /* 空指针错误 */
#define ERR_MEMORY_ALLOCATE 5 /* 内存分配错 */
#define ERR_NULL_STACK 6 /* 栈元素为空 */
#define ERR_PARA 7 /* 函数参数错 */
#define ERR_OPEN_FILE 8 /* 打开文件错 */
#define ERR_NULL_QUEUE 9 /* 队列为空错 */
#define ERR_FULL_QUEUE 10 /* 队列为满错 */
#define ERR_NOT_FOUND 11 /* 表项不存在 */
typedef int Status; /* Status 是函数的类型,其值是函数结果状态代码,如 RET_OK 等 */
typedef int Bollean; /* Boolean 是布尔类型,其值是 TRUE 或 FALSE */
#endif // !STATUS_H
2) linkList.h
/* 具有实用意义的线性链表(带头结点)实现头文件 */
#ifndef LINKLIST_H
#define LINKLIST_H
#include "status.h"
typedef struct {
int occurrTime; /* 事件发生时刻 */
int occurrType; /* 事件类型,QUEUE_NUM 表示到达事件,0 至 QUEUE_NUM - 1表示 QUEUE_NUM 个窗口的离开事件 */
} Event, ElemType;
typedef struct LNode {
ElemType data;
struct LNode *next;
} LNode, *Link, *Position;
typedef struct LinkList {
Link head;
Link tail;
int length;
} LinkList;
typedef LinkList EventList;
/* 分配由指向的值为 e 的结点,并返回 OK;若分配失败, 则返回 NULL */
Link MakeNewLNode(ElemType e);
/* 释放 p 所指结点 */
void FreeLNode(Link *p);
/* 构造一个空的线性链表 */
Status InitList(LinkList *L);
/* 将线性链表 L 重置为空表,并释放原链表的结点空间 */
Status ClearList(LinkList *L);
/* 销毁线性链表 L,L 不再存在 */
Status DestroyList(LinkList *L);
/* h 指向 L 的一个结点,把 head 当做头结点,将 s 所指结点插入在第一个结点之前 */
Status InsFirst(LinkList *L, Link head, Link s);
/* h 指向 L 的一个结点,把 h 当做头结点,删除链表中的第一个结点并以 q 返回
若链表为空( h 指向尾结点),q = NULL,返回 FALSE */
Status DelFirst(LinkList *L, Link head, Link *q);
/* 将指针 s(s->data 为第一个数据元素)所指(彼此以指针相链,以 NULL 结尾)的一
串结点链接在线性链表L的最后一个结点之后,并改变链表 L 的尾指针指向新的尾结点 */
Status Append(LinkList *L, Link s);
/* 已知 p 指向线性链表 L 中的一个结点,返回 p 所指结点的直接前驱的位置若无前驱
则返回 NULL */
Position PriorPos(LinkList L, Link p);
/* 删除线性链表 L 中的尾结点并以 q 返回,改变链表 L 的尾指针指向新的尾结点 */
Bollean Remove(LinkList *L, Link *q);
/* 已知 *p 指向线性链表 L 中的一个结点,将 s 所指结点插入在 *p 所指结点之前
并修改指针 p 指向新插入的结点 */
Status InsBefore(LinkList *L, Link *p, Link s);
/* 已知 *p 指向线性链表 L 中的一个结点,将 s 所指结点插入在 *p 所指结点之后
并修改指针 p 指向新插入的结点 */
Status InsAfter(LinkList *L, Link *p, Link s);
/* 已知 p 指向线性链表中的一个结点,用 e 更新 p 所指结点中数据元素的值 */
Status SetCurrElem(Link p, ElemType e);
/* 已知 p 指向线性链表中的一个结点,返回 p 所指结点中数据元素的值 */
ElemType GetCurrElem(Link p);
/* 若线性链表 L 为空表,则返回 TRUE,否则返回 FALSE */
Bollean ListEmpty(LinkList L);
/* 返回线性链表 L 中元素个数 */
int ListLength(LinkList L);
/* 返回线性链表 L 中头结点的位置 */
Position GetHead(LinkList L);
/* 返回线性链表 L 中最后一个结点的位置 */
Position GetLast(LinkList L);
/* 已知 p 指向线性链表 L 中的一个结点,返回 p 所指结点的直接后继的位置
若无后继,则返回 NULL */
Position NextPos(Link p);
/* 返回 p 指示线性链表 L 中第 i 个结点的位置,并返回 OK,i 值不合法时
返回 ERROR, i = 0 为头结点 */
Status LocatePos(LinkList L, int i, Link *p);
/* 返回线性链表 L 中第 1 个与 e 满足函数 compare() 判定关系的元素的位
置若不存在这样的元素,则返回 NULL */
Position LocateElem(LinkList L, ElemType e, Bollean(*compare)(ElemType, ElemType));
/* 依次对 L 的每个数据元素调用函数 visit()。一旦 visit() 失败,则操作失败 */
Status ListTraverse(LinkList L, void(*visit)(ElemType));
/* 已知 L 为有序线性链表,将元素 e 按非降序插入在 L 中 */
Status InsertAscend(LinkList *L, ElemType e, int(*compare)(ElemType, ElemType));
/* 若升序链表 L 中存在与 e 满足判定函数 compare() 取值为 0 的元素,则 q 指示 L 中
第一个值为 e 的结点的位置,并返回 TRUE;否则 q 指示第一个与 e 满足判定函数
compare() 取值 > 0 的元素的前驱的位置, 并返回 FALSE */
Bollean LocateElemP(LinkList L, ElemType e, Position *q, int(*compare)(ElemType, ElemType));
#endif // !LINKLIST_H
3) linkList.c
/* 具有实用意义的线性链表(带头结点)实现源文件 */
#include "linkList.h"
#include <stdio.h>
#include <stdlib.h>
/* 分配由指向的值为 e 的结点,并返回 OK;若分配失败, 则返回 NULL */
Link MakeNewLNode(ElemType e)
{
Link newLNode = (Link)malloc(sizeof(LNode));
CHECK_NULL(newLNode)
newLNode->data = e;
newLNode->next = NULL;
return newLNode;
}
/* 释放 p 所指结点 */
void FreeLNode(Link *p)
{
free(*p);
*p = NULL;
}
/* 构造一个空的线性链表 */
Status InitList(LinkList *L)
{
Link p = (Link)malloc(sizeof(LNode));
CHECK_VALUE(!p, ERR_MEMORY_ALLOCATE)
p->next = NULL;
(*L).head = (*L).tail = p;
(*L).length = 0;
return RET_OK;
}
/* 将线性链表 L 重置为空表,并释放原链表的结点空间 */
Status ClearList(LinkList *L)
{
CHECK_VALUE(!L, ERR_NULL_PTR)
if ((*L).head == (*L).tail) {
return RET_OK;
}
Link p, q;
p = (*L).head->next;
(*L).head->next = NULL;
while (p != (*L).tail) {
q = p;
p = p->next;
free(q);
}
free(p);
(*L).tail = (*L).head;
(*L).length = 0;
return RET_OK;
}
/* 销毁线性链表 L,L 不再存在 */
Status DestroyList(LinkList *L)
{
ClearList(L);
FreeLNode(&((*L).head));
(*L).tail = NULL;
return RET_OK;
}
/* h 指向 L 的一个结点,把 head 当做头结点,将 s 所指结点插入在第一个结点之前 */
Status InsFirst(LinkList *L, Link head, Link s)
{
s->next = head->next;
head->next = s;
if (head == (*L).tail) {
(*L).tail = head->next;
}
++((*L).length);
return RET_OK;
}
/* h 指向 L 的一个结点,把 h 当做头结点,删除链表中的第一个结点并以 q 返回
若链表为空( h 指向尾结点),q = NULL,返回 FALSE */
Status DelFirst(LinkList *L, Link head, Link *q)
{
*q = head->next;
if (!(*q)) {
return FALSE;
}
head->next = (*q)->next;
if (!(head->next)) {
(*L).tail = head;
}
--(*L).length;
return RET_OK;
}
/* 将指针 s(s->data 为第一个数据元素)所指(彼此以指针相链,以 NULL 结尾)的一
串结点链接在线性链表L的最后一个结点之后,并改变链表 L 的尾指针指向新的尾结点 */
Status Append(LinkList *L, Link s)
{
int length = 1;
(*L).tail->next = s;
while (s->next) {
++length;
s = s->next;
}
(*L).tail = s;
(*L).length += length;
return RET_OK;
}
/* 已知 p 指向线性链表 L 中的一个结点,返回 p 所指结点的直接前驱的位置若无前驱
则返回 NULL */
Position PriorPos(LinkList L, Link p)
{
Link q = L.head->next;
if (p == q) {
return NULL;
}
while (q->next != p) {
q = q->next;
}
return q;
}
/* 删除线性链表 L 中的尾结点并以 q 返回,改变链表 L 的尾指针指向新的尾结点 */
Bollean Remove(LinkList *L, Link *q)
{
CHECK_VALUE(!L, ERR_NULL_PTR)
if ((*L).length == 0) {
*q = NULL;
return FALSE;
}
Link p = (*L).head;
while (p->next != (*L).tail) {
p = p->next;
}
*q = (*L).tail;
p->next = NULL;
(*L).tail = p;
--((*L).length);
return TRUE;
}
/* 已知 *p 指向线性链表 L 中的一个结点,将 s 所指结点插入在 *p 所指结点之前
并修改指针 *p 指向新插入的结点 */
Status InsBefore(LinkList *L, Link *p, Link s)
{
Link q = PriorPos(*L, *p);
if (!q) {
q = (*L).head;
}
s->next = *p;
q->next = s;
*p = s;
++((*L).length);
return RET_OK;
}
/* 已知 *p 指向线性链表 L 中的一个结点,将 s 所指结点插入在 *p 所指结点之后
并修改指针 p 指向新插入的结点 */
Status InsAfter(LinkList *L, Link *p, Link s)
{
if (*p == (*L).tail) {
(*L).tail = s;
}
s->next = (*p)->next;
(*p)->next = s;
*p = s;
++((*L).length);
return RET_OK;
}
/* 已知 p 指向线性链表中的一个结点,用 e 更新 p 所指结点中数据元素的值 */
Status SetCurrElem(Link p, ElemType e)
{
p->data = e;
return RET_OK;
}
/* 已知 p 指向线性链表中的一个结点,返回 p 所指结点中数据元素的值 */
ElemType GetCurrElem(Link p)
{
return p->data;
}
/* 若线性链表 L 为空表,则返回 TRUE,否则返回 FALSE */
Bollean ListEmpty(LinkList L)
{
return (L.length == 0) ? TRUE : FALSE;
}
/* 返回线性链表 L 中元素个数 */
int ListLength(LinkList L)
{
return L.length;
}
/* 返回线性链表 L 中头结点的位置 */
Position GetHead(LinkList L)
{
return L.head;
}
/* 返回线性链表 L 中最后一个结点的位置 */
Position GetLast(LinkList L)
{
return L.tail;
}
/* 已知 p 指向线性链表 L 中的一个结点,返回 p 所指结点的直接后继的位置
若无后继,则返回 NULL */
Position NextPos(Link p)
{
return p->next;
}
/* 返回 p 指示线性链表 L 中第 i 个结点的位置,并返回 OK,i 值不合法时
返回 ERROR, i = 0 为头结点 */
Status LocatePos(LinkList L, int i, Link *p)
{
CHECK_VALUE((i < 0 || i > L.length), ERR_PARA)
*p = L.head;
for (int j = 0; j < i; ++j) {
*p = (*p)->next;
}
return RET_OK;
}
/* 返回线性链表 L 中第 1 个与 e 满足函数 compare() 判定关系的元素的位
置若不存在这样的元素,则返回 NULL */
Position LocateElem(LinkList L, ElemType e, Bollean(*compare)(ElemType, ElemType))
{
Link p = L.head->next;
while ((p) && !(compare(p->data, e))) {
p = p->next;
}
return p;
}
/* 依次对 L 的每个数据元素调用函数 visit()。一旦 visit() 失败,则操作失败 */
Status ListTraverse(LinkList L, void(*visit)(ElemType))
{
Link p = L.head->next;
for (int i = 0; i < L.length; ++i) {
visit(p->data);
p = p->next;
}
return RET_OK;
}
/* 已知 L 为有序线性链表,将元素 e 按非降序插入在 L 中 */
Status InsertAscend(LinkList *L, ElemType e, int(*compare)(ElemType, ElemType))
{
Link q = (*L).head;
Link p = q->next;
while ((p) && (compare(p->data, e) < 0)) {
q = p;
p = p->next;
}
Link newLNode = MakeNewLNode(e);
q->next = newLNode;
newLNode->next = p;
++((*L).length);
if (!p) {
(*L).tail = newLNode;
}
return RET_OK;
}
/* 若升序链表 L 中存在与 e 满足判定函数 compare() 取值为 0 的元素,则 q 指示 L 中
第一个值为 e 的结点的位置,并返回 TRUE;否则 q 指示第一个与 e 满足判定函数
compare() 取值 > 0 的元素的前驱的位置, 并返回 FALSE */
Bollean LocateElemP(LinkList L, ElemType e, Position *q, int(*compare)(ElemType, ElemType))
{
Link pos = L.head;
Link p = pos->next;
while ((p) && (compare(p->data, e) < 0)) {
pos = p;
p = p->next;
}
if ((!p) || (compare(p->data, e) > 0)) {
*q = pos;
return FALSE;
}
*q = p;
return TRUE;
}
4) sqQueue.h
/* 顺序队列实现头文件 */
#ifndef SQQUEUE_H
#define SQQUEUE_H
#include "status.h"
#define MAX_QUEUE_SIZE 5
typedef struct {
int arriveTime; /* 到达时刻 */
int duration; /* 办理事务所需时间 */
} QElemType;
typedef struct {
QElemType *base;
int front;
int rear;
} SqQueue;
/* 构造一个空队列 Q */
Status InitQueue(SqQueue *Q);
/* 销毁队列 Q */
Status DestroyQueue(SqQueue *Q);
/* 将 Q 清为空队列 */
void ClearQueue(SqQueue *Q);
/* 若队列 Q 为空队列,则返回 TRUE,否则返回 FALSE */
Bollean QueueEmpty(const SqQueue *Q);
/* 返回 Q 的元素个数,即队列的长度 */
int QueueLength(const SqQueue *Q);
/* 若队列不空,则用 e 返回 Q 的队头元素,并返回 OK */
Status GetQueueHead(const SqQueue *Q, QElemType *e);
/* 插入元素 e 为 Q 的新的队尾元素 */
Status EnQueue(QElemType e, SqQueue *Q);
/* 若队列不空,则删除 Q 的队头元素,用 e 返回其值,并返回 OK */
Status DeQueue(SqQueue *Q, QElemType *e);
/* 从队头到队尾依次对队列 Q 中每个元素调用函数 vi() */
Status QueueTraverse(const SqQueue *Q, void(*vi)(QElemType));
#endif // !SQQUEUE_H
5) sqQueue.c
/* 顺序队列实现源文件 */
#include "sqQueue.h"
#include <stdio.h>
#include <stdlib.h>
static Bollean QueueFull(const SqQueue *Q)
{
return (Q->rear >= MAX_QUEUE_SIZE) ? TRUE : FALSE;
}
/* 构造一个空队列 Q */
Status InitQueue(SqQueue *Q)
{
Q->base = (QElemType *)malloc(sizeof(QElemType) * MAX_QUEUE_SIZE);
if (!Q->base) {
printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_MEMORY_ALLOCATE);
return ERR_MEMORY_ALLOCATE;
}
Q->front = Q->rear = 0;
return RET_OK;
}
/* 销毁队列 Q */
Status DestroyQueue(SqQueue *Q)
{
if (Q->base) {
free(Q->base);
}
Q->base = NULL;
Q->front = Q->rear = 0;
return RET_OK;
}
/* 将 Q 清为空队列 */
void ClearQueue(SqQueue *Q)
{
Q->front = Q->rear = 0;
}
/* 若队列 Q 为空队列,则返回 TRUE,否则返回 FALSE */
Bollean QueueEmpty(const SqQueue *Q)
{
return (Q->front == Q->rear) ? TRUE : FALSE;
}
/* 返回 Q 的元素个数,即队列的长度 */
int QueueLength(const SqQueue *Q)
{
return (Q->rear - Q->front);
}
/* 若队列不空,则用 e 返回 Q 的队头元素,并返回 OK */
Status GetQueueHead(const SqQueue *Q, QElemType *e)
{
if (Q->front == Q->rear) {
printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_NULL_QUEUE);
return ERR_NULL_QUEUE;
}
*e = *(Q->base + Q->front);
return RET_OK;
}
/* 插入元素 e 为 Q 的新的队尾元素 */
Status EnQueue(QElemType e, SqQueue *Q)
{
if (QueueFull(Q)) {
Q->base = (QElemType *)realloc(Q->base, sizeof(QElemType) * (unsigned long long)(Q->rear + 1));
if (!Q->base) {
printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_MEMORY_ALLOCATE);
return ERR_MEMORY_ALLOCATE;
}
}
Q->base[Q->rear] = e;
Q->rear = ++(Q->rear);
return RET_OK;
}
/* 若队列不空,则删除 Q 的队头元素,用 e 返回其值,并返回 OK */
Status DeQueue(SqQueue *Q, QElemType *e)
{
if (QueueEmpty(Q)) {
printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_NULL_QUEUE);
return ERR_NULL_QUEUE;
}
*e = Q->base[Q->front];
Q->front = Q->front + 1;
return RET_OK;
}
/* 从队头到队尾依次对队列 Q 中每个元素调用函数 vi() */
Status QueueTraverse(const SqQueue *Q, void(*vi)(QElemType))
{
int pos = Q->front;
while (pos != Q->rear) {
vi(Q->base[pos]);
++pos;
}
return RET_OK;
}
6) algorithm.h
/* 算法定义头文件 */
#ifndef ALGORITHM_H
#define ALGORITHM_H
#include "sqQueue.h"
#include "linkList.h"
#define QUEUE_NUM 4 /* 银行队列数, 0 ~ 3 表示队列,4 表示到达事件 */
#define ARRIVE_ADJECENT_MAX_TIME 5 /* 两相邻到达的客户的时间间隔最大值 */
#define SERVICE_MAX_TIME 30 /* 每个客户办理业务的时间最大值 */
#define CLOSE_TIME 100
void Bank_Simulation(void);
#endif // !ALGORITHM_H
7) algorithm.c
/* 算法实现源文件 */
#include "algorithm.h"
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
/* 依事件 a 的发生时刻 = 、> 、< 事件 b 的发生时刻分别返回 0、1 、-1 */
int Comp(Event a, Event b)
{
return (a.occurrTime == b.occurrTime) ? 0 : ((a.occurrTime > b.occurrTime) ? 1 : -1);
}
/* 生成所需随机数 */
void Random(int *durTime, int *interTime)
{
srand((unsigned)time(NULL));
*durTime = rand() % SERVICE_MAX_TIME + 1; /* 1 到 SERVICE_MAX_TIME 之间的随机数 */
*interTime = rand() % ARRIVE_ADJECENT_MAX_TIME + 1; /* 1 到 ARRIVE_ADJECENT_MAX_TIME 之间的随机数 */
}
/* 返回最短队列的序号 */
int MinimumOrder(SqQueue Q[], int queueNum)
{
int minQueueOrder = 0;
int minLength = QueueLength(Q);
for (int i = 1; i < queueNum; ++i) {
int queueLength = QueueLength(Q + i);
if (queueLength < minLength) {
minLength = queueLength;
minQueueOrder = i;
}
}
return minQueueOrder;
}
/* 初始化操作 */
Status OpenForDay(int queueNum, EventList *eventList, SqQueue queueList[])
{
CHECK_VALUE(!eventList, ERR_NULL_PTR)
InitList(eventList);
Event event = { 0 };
event.occurrTime = 0; /* 设定第一个客户到达事件 */
event.occurrType = queueNum; /* 到达 */
InsertAscend(eventList, event, Comp);
for (int i = 0; i < queueNum; ++i) {
InitQueue(queueList + i);
}
return RET_OK;
}
/* The point is the time line */
void CustomerArrived(const Event *event, SqQueue queueList[], EventList *eventList)
{
/* Next customer arrived */
int durTime = 0, interTime = 0;
Random(&durTime, &interTime);
Event eventTemp = { 0 };
eventTemp.occurrTime = event->occurrTime + interTime;
eventTemp.occurrType = QUEUE_NUM;
if (eventTemp.occurrTime < CLOSE_TIME) {
InsertAscend(eventList, eventTemp, Comp);
}
/* The customer into the queue */
int minQueueOrder = MinimumOrder(queueList, QUEUE_NUM);
QElemType costomer = { 0 };
costomer.arriveTime = event->occurrTime;
costomer.duration = durTime;
EnQueue(costomer, queueList + minQueueOrder);
/* Ensure the time when the customer will leave, before the queue is empty */
if (QueueLength(queueList + minQueueOrder) == 1) {
eventTemp.occurrTime = event->occurrTime + durTime;
eventTemp.occurrType = minQueueOrder;
InsertAscend(eventList, eventTemp, Comp);
}
}
void CustomerDeparture(const Event *event, SqQueue queueList[], int *totalTime, EventList *eventList, int *customerNum)
{
int i = event->occurrType;
QElemType customer = { 0 };
DeQueue(queueList + i, &customer);
*totalTime += (event->occurrTime - customer.arriveTime);
++(*customerNum);
Event eventTemp = { 0 };
/* before the queue is not empty, record the leave event */
if (!QueueEmpty(queueList + i)) {
GetQueueHead(queueList + i, &customer);
eventTemp.occurrTime = event->occurrTime + customer.duration;
eventTemp.occurrType = i;
InsertAscend(eventList, eventTemp, Comp);
}
}
void Bank_Simulation(void)
{
EventList eventList;
Event event = { 0 };
SqQueue queueList[QUEUE_NUM] = { 0 };
OpenForDay(QUEUE_NUM, &eventList, queueList);
Link p;
int totalTime = 0;
int customerNum = 0;
while (!ListEmpty(eventList)) {
DelFirst(&eventList, GetHead(eventList), &p);
event.occurrTime = GetCurrElem(p).occurrTime;
event.occurrType = GetCurrElem(p).occurrType;
/* The time that event occurr beyond close time. */
if (event.occurrTime > CLOSE_TIME) {
break;
}
if (event.occurrType == QUEUE_NUM) {
CustomerArrived(&event, queueList, &eventList);
} else {
CustomerDeparture(&event, queueList, &totalTime, &eventList, &customerNum);
}
}
DestroyList(&eventList);
for (int i = 0; i < QUEUE_NUM; ++i) {
DestroyQueue(queueList + i);
}
printf("Total customers: %d, total time: %d, consume %.2f minutes per people\n", customerNum, totalTime,
(float)totalTime / (float)customerNum);
}
8) main.c
#include "algorithm.h"
int main(void)
{
Bank_Simulation();
return 0;
}
3. 运行示例