【数据结构】(五)(循环)队列,链式队列(银行排队(离散事件模拟)算法)

本文介绍了数据结构中的队列,包括循环队列和链式队列的概念及其在银行排队(离散事件模拟)问题中的应用。通过C++和C语言展示了队列的创建、入队、出队操作,并详细解释了银行排队问题的算法实现,涉及到事件处理的链表数据结构。

【数据结构】(五)队列,链式队列(银行排队算法)

(一)队列:(如果用循环将使算法时间变得复杂,所以为了提高运算效率,引入队列
在这里插入图片描述
(1)假溢出:(为了解决假溢出,引入循环队列
在这里插入图片描述
(2)循环队列:(判断空满条件
在这里插入图片描述
其中方法二:
在这里插入图片描述

(3)队列的创建:(所有的引用修改都是为了可以传出去而使用的)
C++:
在这里插入图片描述
C语言:
在这里插入图片描述
(4)操作:
入队:
在这里插入图片描述
出队:
在这里插入图片描述
在主函数中输出item:
在这里插入图片描述
改为C++中语句:
在这里插入图片描述
读取对头元素:
在这里插入图片描述
(二)链式队列:
C++:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(2)区别:
在这里插入图片描述
(三)银行排队(离散事件模拟)问题:
在这里插入图片描述
表示:
如16,19,23都表示客户所用时间;
例如:16 2 -->表示在第二个窗口花了16分钟,对应5 11(排队5分钟,业务11分钟)
在这里插入图片描述
算法:(先图解,最后会有对应代码块)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
还需要一个链表来存储和处理事件:
在这里插入图片描述
在这里插入图片描述
操作:cpp
在这里插入图片描述
在这里插入图片描述
代码:
List.h文件

#pragma once
#include <iostream>
using namespace std;
#include <string>


//*************----《链表初始化定义》----***********//

typedef struct evNode
{
	int occurtime;   //事件发生时间
	int nType;       //事件类型:-1表示到达,0~3表示四个窗口
	struct evNode* next;
}evNode;

class EventList
{
private:
	evNode* head;   //头指针
	int length;
public:
	EventList()   //构造函数
	{
		head = new evNode;
		head->next = NULL;
		length = 0;
	}
	~EventList()  //析构函数,删除节点,清空
	{
		//释放再堆区开辟的所有节点
		//保存释放的下一个节点
		while (head->next)
		{
			evNode* p = head->next->next;
			delete(head->next);
			head->next = p;
		}
		length = 0;
	}
	bool isEmpty()  //判断是否为空
	{
		if (length == 0)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	void addNode(EventList List, evNode* eventItem)   //向链表插入数据
	{
		//插入是要按离开或者到达的时间顺序进行插入
		evNode* p = new evNode;
		p = eventItem;
		//对链表进行遍历,找到新用户该插入的位置
		evNode* qi, * hou;    //前后指针,用于查找插入排序
		qi = List.head;
		hou = List.head->next;
		while (hou != NULL)
		{
			if (p->occurtime <= hou->occurtime)
			{
				//将新用户节点插在该节点前面
				p->next = hou;
				qi->next = p;
				qi->next->next = hou;
				break;
			}
			qi = hou;
			hou = hou->next;
		}
		qi->next = p;
		length++;
	}
	void deleteNode(EventList List, evNode* firstNode)   //对链表删除数据
	{
		evNode* p = new evNode;
		p = List.head->next;
		firstNode = p;
		List.head = p->next;
		delete(p);
		p = NULL;
		length--;
	}
	void displayNode()  //遍历数据
	{
		evNode* p = head->next;
		while (p)
		{
			if (p->nType == -1)
			{
				cout << "新用户到达时间为:" << p->occurtime << endl;
			}
			else
			{
				cout << p->nType << "号窗口的用户离开时间为:" << p->occurtime << endl;
			}
			p = p->next;
		}
	}
};

Queue.h文件:

#pragma once
#include <iostream>
using namespace std;
#include <string>

                           //************----《队列初始化定义》----************//

//定义银行客户,Client作为队列的data域
typedef struct Client
{
	int arrivetime;   //客户到达银行时间
	int duration;     //客户办理业务时间
}Client;

struct Node
{
	Client data;   //包含客户信息
	struct Node* next; 
};

class Queue       //银行窗口队列类
{
private:
	Node* front;   //头尾指针
	Node* rear;
	int length;   //队列长度,因为插入时会标记不同的窗口队列,所以不必担心length加重的问题
public:
	Queue()     //建立头节点,初始化属性
	{
		front = new Node;
		rear = front;
		front->next = NULL;
		length = 0;
	}
	~Queue()     //释放队列空间
	{
		if (front == rear)
		{
			return;
		}
		else
		{
			while (front)   //直到删完为止
			{
				rear = front->next;
				delete(front);
				front = rear;
			}
			length = 0;
		}
	}
	void enQueue(Client person)   //入队
	{
		//不用判断队列是否为满,因为是链式存储
		//头插
		Node* p=new Node;
		p->data = person;
		p->next = NULL;

		rear->next = p;
		rear = p;
		length++;
	}
	bool deQueue()  //出队
	{
		//队列为空,返回假
		if (front == rear)
		{
			return false;
		}
		//进行头删
		Node* p = front->next;
		front->next = p->next;
		//判断删除的是否是最后一个节点,是的话把rear指向头结点
		if (p == rear)
		{
			rear = front;
		}
		delete(p);
		p = NULL;
		length--;
		return true;
	}
	bool getQueue(Client* item)  //获取队头元素到item所☞单元
	{
		if (length == 0)
		{
			cout << "无对头元素" << endl;
			system("pause");
			exit(0);
		}
		else
		{
			Node* p ;
			p = front->next;
			*item = p->data;
			delete(p);
			return true;
		}
	}
	bool isEmpty()     //判断是否为空
	{
		if (length == 0)
		{
			return true;
		}
		else
			return false;
	}
	void clearQueue()   //清空队列
	{
		//清空除头结点以外的节点
		Node* p;
		p = front->next;
		while (p)
		{
			Node* nextNode = p;
			p = p->next;
			delete(nextNode);
		}
		p = NULL;
		//尾指针更新到头指针处
		front->next = NULL;
		rear = front;
		length = 0;
	}
	int queueLength()    //获取元素个数
	{
		return length;
	}
};

cpp文件:

#include "Queue.h"
#include "List.h"
#include<time.h>
#define Closetime 40 //银行关门时间
int findMin(Queue queue[], int len);

//现写出银行业务活动如下:
//模仿
double  simulation()
{
	//为客户服务的总时长
	int totalTime = 0;
	//客户人数
	int customerNum = 0;
	//初始化四个窗口队列
	Queue queue[4];
	//初始化事件链表对象
	EventList List;
	//初始化一个客户结构体
	Client person;
	//设定第一个客户到达的事件
	evNode eventItem = { 0, -1, NULL };
	//将第一个客户放到事件链表中
	List.addNode(List,&eventItem);
	//判断事件链表是否为空,不为空取出事件链表中第一个事件节点,判断是用户到达事件还是用户离开事件
	while (!List.isEmpty())
	{
		evNode firstNode;
		List.deleteNode(List,&firstNode);    //弹出(读取)链表第一个数同时在链表中删除第一个数
		cout << "第一个事件类型:" << firstNode.nType<<"  ";

		if (firstNode.nType == -1)  //-1表示客户刚到
		{
			customerNum++;   //客户人数加一

			//生成随机函数-->服务时间,-->下一位顾客到来时间
			int duringTime = rand() % 30 + 1;   //接受服务时间范围是1到30
			int nextcustomertime = rand() % 20 + 1; //下一个用户到来的间隔时间范围是1到20
			cout << "当前客户接受服务所用时间: " << duringTime << endl;
			//下一个新用户到来的时间---下一个到来事件发生的时间
			 //要判断下一个用户到来的时候,银行有没有关门
			//当前新用户到达时间+间隔时间
			if (firstNode.occurtime < Closetime)
			{
				//设定下一个用户的到达事件插入事件表
				evNode nextPerson;
				//表示下一个顾客到达时间,方式
				nextPerson = { firstNode.occurtime + nextcustomertime,-1,NULL };

				cout << "下一个用户到达时间: " << nextPerson.occurtime << "  ";
				List.addNode(List,&nextPerson);
			}
			//把当前到达的用户,放到当前排队人数最少的队列中
			//若四个队列排队人数相同,就按队列的顺序从下标小的先插入
			int min = findMin(queue, 4);

			cout << "去" << min << "窗口排队最好" << endl;

			//当前客户进入人数最少的队列
			//先对客户结构体进行赋值
			person = { firstNode.occurtime , duringTime };  

			//入队
			queue[min].enQueue(person);

			//入队完了之后,判断当前客户是不是窗口的第一个人,如果是就要把他的离开事件放入事件表中
			if (queue[min].queueLength() == 1)
			{
				evNode Putincust = { firstNode.occurtime + duringTime, min, NULL };
				cout << "用户离开时间为: " << Putincust.occurtime << "  ";
				cout << "离开窗口: " << min <<"\n\n"<< endl;
				//将离开事件插入事件链表中
				List.addNode(List,&Putincust);
			}
		}
		else     //表示处理的是用户离开事件
		{
			//记录该用户从几号窗口离开
			int index = firstNode.nType;
			//获取队头元素
			Client leaver;
			queue[index].getQueue(&leaver);
			//客户离开的时候,要累积客户的逗留时长
			totalTime = leaver.duration;
			//出队
			queue[index].deQueue();

			//判断出完队后,当前队列是否为空,如果为空,就不管
			//如果还有下一个用户,就要把下一个用户的离开事件放入事件表中
			if (queue[index].queueLength() != 0)
			{
				Client nextPer;
				queue[index].getQueue(&nextPer);
				//离开的时间等于到达的时间加上逗留的时间
				evNode tempNode = { nextPer.arrivetime + nextPer.duration,index,NULL };
				List.addNode(List,&tempNode);
			}
		}
		cout << "当前正在处理的事件" << endl;
		cout << endl;
	}
	//计算客户的平均逗留时间
	cout << "\n\n用户访问总数: " << customerNum << "  ";
	cout << "总时间" << totalTime << endl;
	return totalTime / customerNum;
}


int findMin(Queue queue[], int len)      //找出排队人数最少的窗口下标
{
	int min = queue[0].queueLength();
	int Index = 0;
	for (int i = 0; i < len; i++)
	{
		if (min < queue[i].queueLength())
		{
			min = queue[i].queueLength();   //多长
			Index = i;  //那个窗口
		}
		else
		{
			continue;
		}
	}
	return Index;
}


int main()
{
	srand(unsigned int((time(NULL))));   //随机函数
	double num = simulation();
	cout << "用户平均服务时间: " << num << endl;
	system("pause");
	return 0;
}

结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值