datawhale算法与数据结构(上)day4-队列
理论部分
一、队列
- 定义
和栈相反,队列是一种先进先出(FIFO)的线性表。只允许在表的一端进行插入,而在另一端删除元素。在队列中,允许插入的一端叫做队尾,允许删除的一段则称队头。
队列在程序设计中经常出现。一个最典型的例子就是操作系统中的作业排队。在允许多道程序运行的计算机系统中,同时有几个作业运行。如果运行的结果都需要通过通道输出,那就要按请求输出的先后次序排队。
2.用数组实现一个顺序队列
#说明sq.h
#ifndef _SQ_H_
#define _SQ_H_
typedef int dataType;
#define maxSize 100
class sq
{
public:
sq();
//~sq();
void push(dataType var);
void pop();
dataType front();
bool isEmpty();
bool isFull();
private:
dataType queue[maxSize];
int head;
int tail;
};
#endif
#顺序队列类的定义
#include <iostream>
#include "sq.h"
using namespace std;
sq::sq()
{
head = -1;
tail = -1;
}
void sq::push(dataType var)
{
queue[++tail] = var;
if(tail == 0)
{
head = 0;
}
}
void sq::pop()
{
++head;
}
dataType sq::front()
{
return queue[head];
}
bool sq::isEmpty()
{
bool flag = head > tail; //当head和tail不为-1时
if(head == -1 && tail == -1) //当head=tail=-1时
{
flag = true;
}
if(flag)
{
head = tail = -1;
}
return flag;
}
bool sq::isFull()
{
return tail == maxSize-1;
}
#main.cpp
#include <iostream>
#include "sq.h"
using namespace std;
int main()
{
sq exp;
int i = 0;
for(i=0;i<maxSize+10;++i)
{
if(!exp.isFull())
{
exp.push(i);
}
}
for(i=0;i<maxSize+20;++i)
{
if(!exp.isEmpty())
{
cout<<exp.front()<<endl;
exp.pop();
}
}
if(exp.isEmpty())
{
cout<<"队列已空!"<<endl;
}
return 0;
}
- 数组实现一个循环队列
#include
using namespace std;
template
class queue
{
private:
int front;//对尾的下标
int rear;
int Maxsize;
T *q;
public:
queue(int Msize) :Maxsize(Msize)//:作用是MAXSZE变量的初始化
{
q = new T[Maxsize];//q指向分配的数组首地址,也就是数组名
front = rear = 0;
}
~queue(){ delete q; }
int size(){ return (rear - front + Maxsize) % Maxsize; }
bool enqueue(const T& v)
{
if (size() == Maxsize)
{
cout << "队列已满" << endl;
return false;
}
else
{
q[rear] = v;//这里有一些别扭,指针竟然可以作为数组的下标
rear = (rear + 1) % Maxsize;
return true;
}
}
int dequeue(T v)
{
if (size() == 0)
{
cout << "队列空" << endl;
return 0;
}
else
{
v=q[front];
q[front] = NULL;
front = (front + 1) % Maxsize;//因为是数组所以不需要释放,只需令其值为Null
return v;
}
}
};
int main()
{
char m;
queue q1(20);
cout << "输入a入队,b出队,c退出!" << endl;
cin >> m;
while (m != 'c')
{
if (m == 'a')
{
int i,j;
cout << "输入20以下的数字,即需要入队的个数:";
cin >> i;
for (j = 0; j < i; j++)
{
int k;
cin >> k;
q1.enqueue(k);
cout << "这是当前队列中的元素个数:" << q1.size() << endl;
}
cout << "队满,请输入b进行出队操作或输入c退出!" << endl;
cin >> m;//为了使操作连续
}
if (m == 'b')
{
while (q1.size() != 0)
{
int n;
cout << "请输入你要出队元素赋给哪个数字(注:队列中必须存在)" << endl;
cin >> n;
cout <<" 出队元素赋给输入的数字,即n=" << q1.dequeue(n) << endl;
cout << "这是当前队列中的元素个数" << q1.size() << endl;
}
cout << "请先输入a:入队一些元素,或输入c退出操作!" << endl;
cin >> m;//为了使操作连续
}
}
}
- 数组实现一个链式队列
#include
using namespace std;
template
class queue
{
private:
int front;//对尾的下标
int rear;
int Maxsize;
T *q;
public:
queue(int Msize) :Maxsize(Msize)//:作用是MAXSZE变量的初始化
{
q = new T[Maxsize];//q指向分配的数组首地址,也就是数组名
front = rear = 0;
}
~queue(){ delete q; }
int size(){ return (rear - front + Maxsize) % Maxsize; }
bool enqueue(const T& v)
{
if (size() == Maxsize)
{
cout << "队列已满" << endl;
return false;
}
else
{
q[rear] = v;//这里有一些别扭,指针竟然可以作为数组的下标
rear = (rear + 1) % Maxsize;
return true;
}
}
int dequeue(T v)
{
if (size() == 0)
{
cout << "队列空" << endl;
return 0;
}
else
{
v=q[front];
q[front] = NULL;
front = (front + 1) % Maxsize;//因为是数组所以不需要释放,只需令其值为Null
return v;
}
}
};
int main()
{
char m;
queue q1(20);
cout << "输入a入队,b出队,c退出!" << endl;
cin >> m;
while (m != 'c')
{
if (m == 'a')
{
int i,j;
cout << "输入20以下的数字,即需要入队的个数:";
cin >> i;
for (j = 0; j < i; j++)
{
int k;
cin >> k;
q1.enqueue(k);
cout << "这是当前队列中的元素个数:" << q1.size() << endl;
}
cout << "队满,请输入b进行出队操作或输入c退出!" << endl;
cin >> m;//为了使操作连续
}
if (m == 'b')
{
while (q1.size() != 0)
{
int n;
cout << "请输入你要出队元素赋给哪个数字(注:队列中必须存在)" << endl;
cin >> n;
cout <<" 出队元素赋给输入的数字,即n=" << q1.dequeue(n) << endl;
cout << "这是当前队列中的元素个数" << q1.size() << endl;
}
cout << "请先输入a:入队一些元素,或输入c退出操作!" << endl;
cin >> m;//为了使操作连续
}
}
}
练习题
模拟银行服务完成程序代码。
目前,在以银行营业大厅为代表的窗口行业中大量使用排队(叫号)系统,该系统完全模拟了人群排队全过程,通过取票进队、排队等待、叫号服务等功能,代替了人们站队的辛苦。
排队叫号软件的具体操作流程为:
-
顾客取服务序号
当顾客抵达服务大厅时,前往放置在入口处旁的取号机,并按一下其上的相应服务按钮,取号机会自动打印出一张服务单。单上显示服务号及该服务号前面正在等待服务的人数。 -
服务员工呼叫顾客
服务员工只需按一下其柜台上呼叫器的相应按钮,则顾客的服务号就会按顺序的显示在显示屏上,并发出“叮咚”和相关语音信息,提示顾客前往该窗口办事。当一位顾客办事完毕后,柜台服务员工只需按呼叫器相应键,即可自动呼叫下一位顾客。
编写程序模拟上面的工作过程,主要要求如下:
- 程序运行后,当看到“请点击触摸屏获取号码:”的提示时,只要按回车键,即可显示“您的号码是:XXX,您前面有YYY位”的提示,其中XXX是所获得的服务号码,YYY是在XXX之前来到的正在等待服务的人数。
- 用多线程技术模拟服务窗口(可模拟多个),具有服务员呼叫顾客的行为,假设每个顾客服务的时间是10000ms,时间到后,显示“请XXX号到ZZZ号窗口!”的提示。其中ZZZ是即将为客户服务的窗口号。
#include<iostream>
#include <stdlib.h>
#include <time.h>
#include<windows.h>
using namespace std;
int K,N; //定义银行服务窗口个数和一天的客户人数
//定义链表节点
template<class T>
struct chainNode
{
//数据成员
T element;
chainNode<T> *next;
//方法
chainNode(){}
chainNode(const T& element){
this->element=element;
}
chainNode(const T& element,chainNode<T> *next){
this->element=element;
this->next=next;
}
};
//定义队列
template<class T>
class queue{
public:
queue(); //初始化队列
~queue(){}; //析构函数
bool empty(){ //判断队列是否为空
return queueSize==0;
}
int size(){ //返回队列中元素的个数
return queueSize;
}
T& front(){ //返回队列首元素
return queueFront->element;
}
T& back(){ //返回队列尾元素
return queueBack->element;
}
chainNode<T> * begin(){
return queueFront;
}
void pop(); //删除首元素
void push(const T& theElement);
private:
int queueSize; //队列长度
chainNode<T> *queueFront; //指向队列第一个元素的指针
chainNode<T> *queueBack; //指向队列最后一个元素的指针
};
template<class T>
queue<T>::queue(){
queueFront=queueBack=NULL;
queueSize=0;
}
template<class T>
void queue<T>::pop(){
if(queueFront==NULL) {
cout<<"you queue is empty!";
return;
}
chainNode<T> *nextNode=queueFront->next;
delete queueFront;
queueFront=nextNode;
queueSize--;
}
template<class T>
void queue<T>::push(const T& theElement){
//申请新元素节点
chainNode<T> *newNode=new chainNode<T>(theElement,NULL);
//把新节点插入队尾
if(queueSize==0) queueFront=newNode; //队空
else queueBack->next=newNode; //队非空
queueBack=newNode;
queueSize++;
}
//定义时间
struct thetime{
int h; //时
int m; //分
};
//定义每个客户的到达银行时间,等待时间,开始办理业务时间,业务处理时间,结束业务时间(离开银行时间)
struct customers{
struct thetime arrive_time; //到达银行时间
struct thetime wait_time; //等待时间
struct thetime start_time; //开始办理业务时间
int business_time; //业务处理时间(假设业务处理时间为整数,且处理时间范围为10-40min)
struct thetime end_time; //结束办理业务时间
int in_bank_number; //客户进入银行的序号
};
//展示窗口服务情况
void show_queue(queue<customers> cus_queue[]){
Sleep(1000);
for(int i = 0;i < K;i++) {
cout<<"窗口"<<i+1<<": ";
chainNode<customers> *m = cus_queue[i].begin();
while(m != NULL) {
cout<<"客户"<<m->element.in_bank_number<<" ";
m = m -> next;
}
cout<<endl;
}
cout<<endl;
}
//用户到达银行时间表函数
void customers_time(struct customers &c){
//随机产生客户到达银行时间
c.arrive_time.h=9+rand()%8;
c.arrive_time.m=rand()%60;
//随机产生客户业务处理时间
//c.business_time=10+rand()%31;
}
//按用户的到达时间将用户的先后顺序进行排序(由于用户的到达时间是随机产生的)
void customer_sort(customers customer[]){
int max_time_index; //记录客户到达的最晚时间对应的下标
customers max_time_cus,swap_cus;
//采用选择排序进行排序
for(int i=N-1;i>0;i--)
{
max_time_cus=customer[i];
max_time_index=i;
//找出到达时间最晚的
for(int j=0;j<i;j++)
{
if((customer[j].arrive_time.h)*60+customer[j].arrive_time.m > (max_time_cus.arrive_time.h)*60+max_time_cus.arrive_time.m)
{
max_time_cus=customer[j];
max_time_index=j;
}
}
if(i!=max_time_index){
//swap部分
swap_cus=customer[i];
customer[i]=max_time_cus;
customer[max_time_index]=swap_cus;
}
}
}
//判断客户需要去哪个窗口排队,即算出等待时间最少的队列
int judge_queue_in(queue<customers> cus_queue[],customers &customer,int each_queue_cus_number[]) {
//将用户在每个队列需要等待的时间放在一个数组里
int each_queue_wait_time[K];
for(int i=0;i<K;i++) {
//客户的等待时间取决于他队伍最后一个人的结束办理时间
int wait_h=cus_queue[i].back().end_time.h-customer.arrive_time.h;
each_queue_wait_time[i]=wait_h*60+cus_queue[i].back().end_time.m-customer.arrive_time.m;
}
//找出需要时间最少的队列
int min_time_queue_index=0;
for(int j=1;j<K;j++) {
if(each_queue_wait_time[j] < each_queue_wait_time[min_time_queue_index])
min_time_queue_index=j;
}
//定义客户的各项数据
customer.business_time=10+rand()%31;
customer.wait_time.h=each_queue_wait_time[min_time_queue_index]/60;
customer.wait_time.m=each_queue_wait_time[min_time_queue_index]%60;
customer.start_time.h=cus_queue[min_time_queue_index].back().end_time.h;
customer.start_time.m=cus_queue[min_time_queue_index].back().end_time.m;
customer.end_time.h=customer.start_time.h+(customer.start_time.m+customer.business_time)/60;
customer.end_time.m=(customer.start_time.m+customer.business_time)%60;
//将客户加入队列
//对银行关门时间到了,还在办理的客户允许继续办理完业务再离开,还在排队的进马上离开
if((customer.start_time.h)*60+customer.start_time.m < 17*60) {
cus_queue[min_time_queue_index].push(customer);
each_queue_cus_number[min_time_queue_index]++;
}
return min_time_queue_index;
}
//判断下一个客户到来时哪个队列的队首客户是否已经办理完业务,并进行出队
void leave_queue(queue<customers> cus_queue[],customers customer){
for(int i=0;i<K;i++) {
while(!cus_queue[i].empty() && (cus_queue[i].front().start_time.h)*60+cus_queue[i].front().start_time.m+
cus_queue[i].front().business_time <= (customer.arrive_time.h)*60+customer.arrive_time.m) {
cout<<"----------客户"<<cus_queue[i].front().in_bank_number<<"离开了窗口"<<i+1<<"----------"<<endl;
cus_queue[i].pop();
}
}
//show_queue(cus_queue);
}
//用户进入队列函数
void customers_in_queue(queue<customers> cus_queue[],customers customer[],int each_queue_cus_number[]) {
//判断哪个窗口是否有空闲的
int queue_number;
for(int i=0;i<N;i++) {
bool queue_free=false;
//每次进入队列判断各队首是否已经办理完业务
leave_queue(cus_queue,customer[i]);
for(int j=0;j<K;j++) {
//窗口中有空闲的情况
if(cus_queue[j].empty())
{
//客户每进入一个队列都需要判断每个队首客户是否已经办理完业务
customer[i].business_time=10+rand()%31;
customer[i].wait_time.h=0;
customer[i].wait_time.m=0;
customer[i].start_time.h=customer[i].arrive_time.h;
customer[i].start_time.m=customer[i].arrive_time.m;
customer[i].end_time.h=customer[i].start_time.h+(customer[i].start_time.m+customer[i].business_time)/60;
customer[i].end_time.m=(customer[i].start_time.m+customer[i].business_time)%60;
cus_queue[j].push(customer[i]);
each_queue_cus_number[j]++;
queue_free=true;
cout<<"----------客户"<<customer[i].in_bank_number<<"到达了窗口"<<j+1<<"----------"<<endl;
cout<<"客户"<<customer[i].in_bank_number<<"需等人数:0"<<endl;
break;
}
}
//窗口中没有空闲的情况
if(queue_free==false){
queue_number = judge_queue_in(cus_queue,customer[i],each_queue_cus_number); //判断哪个队列的等待时间最少
cout<<"----------客户"<<customer[i].in_bank_number<<"到达了窗口"<<queue_number<<"----------"<<endl;
cout<<"客户"<<customer[i].in_bank_number<<"需等人数:"<<cus_queue[queue_number].size()-1<<endl;
}
show_queue(cus_queue);
}
//展示最后客户的离开状态
leave_queue(cus_queue,customer[N]);
show_queue(cus_queue);
}
int main(){
//srand(time(0));
srand((unsigned int)time(NULL)); //使每次编译完后产生的随机数不同
welcom(); //欢迎界面
cout<<"请输入银行服务窗口的个数:";
cin>>K;
cout<<"请输入银行一天的客户人数:";
cin>>N;
customers customer[N];
queue<customers> cus_queue[K];
int each_queue_cus_number[N];
for(int i=0;i<N;i++){
customers_time(customer[i]); //初始用户时间
each_queue_cus_number[i]=0; //初始窗口服务客户个数
cout<<i+1<<" arrive_time: "<<customer[i].arrive_time.h<<": ";
cout<<customer[i].arrive_time.m<<endl;
}
customer_sort(customer); //按客户进入银行的先后顺序进行排序
cout<<"---------------------------after sort---------------------------"<<endl;
for(int i=0;i<N;i++){
//Sleep(2000);
customer[i].in_bank_number = i + 1;
cout<<i+1<<" arrive_time: "<<customer[i].arrive_time.h<<": ";
cout<<customer[i].arrive_time.m<<endl;
}
cout<<"---------------------------begin serve---------------------------"<<endl;
customers_in_queue(cus_queue,customer,each_queue_cus_number); //客户进队列
cout<<"---------------------------end serve---------------------------"<<endl;
cout<<"---------------------------after customer in queue---------------------------"<<endl;
for(int i=0;i<N;i++){
cout<<i+1<<" start_time: "<<customer[i].start_time.h<<":"<<customer[i].start_time.m<<"\t";
cout<<i+1<<" end_time: "<<customer[i].end_time.h<<":"<<customer[i].end_time.m<<"\t";
cout<<i+1<<" bussiness_time: "<<customer[i].business_time<<"min\t";
cout<<i+1<<" wait_time: "<<(customer[i].wait_time.h)*60+customer[i].wait_time.m<<" "<<endl;
}
int max_cus_wait_time=(customer[0].wait_time.h)*60+customer[0].wait_time.m;
int sum_cus_wait_time=max_cus_wait_time;
for(int i=1;i<N;i++){
if((customer[i].wait_time.h)*60+customer[i].wait_time.m > max_cus_wait_time)
max_cus_wait_time=(customer[i].wait_time.h)*60+customer[i].wait_time.m;
sum_cus_wait_time+=(customer[i].wait_time.h)*60+customer[i].wait_time.m;
}
int actual_cus_numbers=0;
for(int i=0;i<K;i++){
cout<<"窗口"<<i+1<<"服务顾客个数: "<<each_queue_cus_number[i]<<endl;
actual_cus_numbers+=each_queue_cus_number[i];
}
cout<<"max_cus_wait_time: "<<max_cus_wait_time<<"min"<<endl;
cout<<"avg_cus_wait_time: "<<(double)sum_cus_wait_time/actual_cus_numbers<<"min"<<endl;
return 0;
}
【来源】
[1] 数据结构
[2] https://blog.youkuaiyun.com/Lulipeng_cpp/article/details/10754495
[3] https://blog.youkuaiyun.com/jx232515/article/details/51483957
[4] https://blog.youkuaiyun.com/jx232515/article/details/51483957
[5] https://blog.youkuaiyun.com/Hachi_Lin/article/details/79774526