第 3 章 栈和队列 (使用线性链表和队列实现银行业务模拟)

news2024/11/23 19:34:35

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. 运行示例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1006023.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小程序类找茬游戏开发:创造富有挑战性和娱乐性的游戏体验

小程序找茬游戏是一种受欢迎的益智娱乐游戏&#xff0c;玩家需要在两幅几乎相同的图片中找出差异。这种类型的游戏结合了观察力和注意力&#xff0c;提供了有趣的挑战。在本文中&#xff0c;我们将讨论如何开发小程序找茬游戏&#xff0c;以及关键特点和开发流程。 小程序找茬…

初露头角!Walrus入选服贸会“数智影响力”数字化转型创新案例

9月5日&#xff0c;由北京市通信管理局、工业和信息化部新闻宣传中心联合主办的“企业数字化转型论坛”在2023中国国际服务贸易交易会期间召开&#xff0c;论坛以“数字化引领 高质量发展”主题&#xff0c;旨在探讨信息技术如何与各行业深度融合&#xff0c;构建数字化转型新格…

C++vector模拟实现

vector模拟实现 1.构造函数2.拷贝构造3.析构赋值运算符重载4.iterator5.modifiers5.1push_back5.2pop_back5.3empty5.4insert5.5erase5.6swap 6.Capacity6.1size6.2capacity6.3reserve6.4resize6.5empty 7.Element access7.1operator[]7.2at 8.在谈reserve vector官方库实现的是…

SpringBoot整合Redis,基于Jedis实现redis各种操作

前言&#xff08;三步教你学会redis&#xff0c;主打一个实用&#xff09; springboot整合redis步骤&#xff0c;并基于jedis对redis数据库进行相关操作&#xff0c;最后分享非常好用、功能非常全的redis工具类。 第一步&#xff1a;导入maven依赖 <!-- springboot整合re…

C++之智能指针shared_ptr死锁问题(二百)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Hadoop生态圈中的Flume数据日志采集工具

Hadoop生态圈中的Flume数据日志采集工具 一、数据采集的问题二、数据采集一般使用的技术三、扩展&#xff1a;通过爬虫技术采集第三方网站数据四、Flume日志采集工具概述五、Flume采集数据的时候&#xff0c;核心是编写Flume的采集脚本xxx.conf六、Flume案例实操1、采集一个网络…

卫星地图-航拍影像-叠加配准套合(ArcGIS版)

卫星地图-航拍影像-叠加配准套合(ArcGIS版) 发布时间&#xff1a;2018-01-17 版权&#xff1a;BIGEMAP 第一步 工具准备 BIGEMAP地图下载器&#xff1a;Bigemap系列产品-GIS行业基础软件kml\shp 相关教程&#xff1a;CAD文件直接导入BIGEMAP进行套合配准&#xff08;推荐&am…

苹果笔不用原装可以吗?苹果ipad触控笔推荐

很多小伙伴在纠结&#xff0c;是否要购买apple pencil呢&#xff1f;但它的价格太过昂贵&#xff0c;很多学生党都消费不起。答案是不一定的要入手apple pencil的。市面上也是有做得相当不错的平替电容笔。现在无纸化已经快成为我们生活中的一部分&#xff0c;它不仅是可以书写…

新网站怎么优化才能提升排名-彻底解决新网站没收录和排名的问题

新网站怎么优化才能提升排名&#xff1f;辛辛苦苦搭建起网站后&#xff0c;却不知如何优化网站提升排名&#xff0c;也没有一个完整的思路和策略&#xff0c;今天做为行业资深SEO的我&#xff0c;来为大家理清SEO思路&#xff0c;彻底解决新网站没收录和排名的问题&#xff01;…

网站不收录没排名降权怎么处理-紧急措施可恢复网站

网站降权对于SEO人员来说是非常致命的打击&#xff0c;因为网站一旦被搜索引擎降权&#xff0c;排名会严重地下降&#xff0c;网站的流量也会大幅下降&#xff0c;直接影响到收益。而且处理不好的话会导致恢复的时间周期无限拉长&#xff0c;所以网站被降权后我们要第一时间采取…

8月客户文章盘点——累计IF 168.4,平均IF 8.42

客户文章一览 凌恩生物以打造国内一流生物公司为目标&#xff0c;在科研测序领域深耕不辍&#xff0c;吸纳多名在生物信息高级技术人员的加盟&#xff0c;参与并完成多个高科技项目。现已在宏组学、基因组、表观遗传以及蛋白代谢等多组学及联合分析领域积累了深厚经验&#xff…

软件测试总结1

1、 什么是软件测试? 答: 软件测试是在规定的条件下对程序进行操作&#xff0c;以发现错误&#xff0c;对软件质量进行评估。 什么是软件测试&#xff1a; 明确地提出了软件测试以检验是否满足需求为目标。 1、保证软件质量的重要手段 预期 ≈ 实际 2、 软件测试的意义 给…

其它机器访问mysql配置

其它机器访问mysql配置 搜索工具 叫 Everything 一、mysql - 改my.ini 刷脚本 也可能叫其他名字 编辑 bind-address0.0.0.0 编辑然后重启一下mysql服务任务管理器-关掉mysql 编辑 搜索 计算机管理-重启mysql服务 编辑 然后 打开查询&#xff0c;并选择mysql数据&#x…

分享微信聊天记录恢复的3个简单方法!

无论身在何处&#xff0c;微信是我们与家人、好友保持紧密联系的好帮手。微信聊天记录不仅仅只是几句话、几张照片……更多的是聊天记录中承载着的美好回忆。如果不小心将聊天记录删除了怎么办&#xff1f;微信聊天记录恢复的方法有哪些&#xff1f;接下来&#xff0c;小编将以…

解决IntelliJ IDEA执行maven打包,执行java -jar命令提示jar中没有主清单属性

问题场景 IDEA执行mvn clean package -DskipTesttrue命令或者借助工具的Maven菜单进行打包操作&#xff0c;然后执行java -jar app.jar命令后&#xff0c;提示jar中没有主清单属性 D:\WorkSpace\demo\target>java -jar demo-SNAPSHOT.jar demo-SNAPSHOT.jar中没有主清单属性…

flex布局中的几个小技巧

1. flex属性一定要写在父级元素的css属性&#xff0c;比如ul里面&#xff0c;不能写到li上面 ul{display:flex; // flex必须写在父元素ul里面justify-content:space-between;height:75px;li{height:75px;line-height:75px;flex:1;padding-left:23px;a{color:$colorB;fo…

3D数字孪生:从3D数据采集到3D内容分析

数字孪生&#xff08;Digital Twin&#xff09;是物理对象、流程或系统的虚拟复制品&#xff0c;用于监控、分析和优化现实世界的对应物。 这些数字孪生在制造、工程和城市规划等领域变得越来越重要&#xff0c;因为它们使我们能够在现实世界中实施改变之前模拟和测试不同的场景…

Linux操作(查询日志)

目录 前言 查看日志 cat less head tail 小结 前言 之前的linux文章属于入门linux,这篇文章主要是linux在后端开发人员中对日志的的运用.对于linux基础掌握不是很好的小伙伴可以先去看看linux基础操作:Linux系统使用(超详细)_linux操作系统使用_陌上 烟雨齐的博客-CSDN博…

Leetcode152. 连续子数组的最大乘积

力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 给你一个整数数组 nums &#xff0c;请你找出数组中乘积最大的非空连续子数组&#xff08;该子数组中至少包含一个数字&#xff09;&#xff0c;并返回该子数组所对应的乘积。 测试用例的答案是一个 32…

Android 系统中适配OAID获取

一、OAID概念 OAID&#xff08;Open Anonymous Identification&#xff09;是一种匿名身份识别标识符&#xff0c; 用于在移动设备上进行广告追踪和个性化广告投放。它是由中国移动通信集 团、中国电信集团和中国联通集团共同推出的一项行业标准 OAID值为一个64位的数字 二、…