相关概念
1.定义:队列是限定仅能在表头进行删除,表尾进行插入的线性表。
2.特点:先进先出
3.队列类型的实现:
链队列、循环队列
真上溢:队列真正满时入队
假上溢:rear已指向队尾,但队列前端仍有空位置
解决假上溢方法:循环队列,利用“模运算”表示循环的含义
入队:Q.rear = (Q.rear+1) % MAXQSIZE
出队:Q.front = (Q.front+1)% MAXQSIZE
5.循环队列队空,队满条件:
队空: Q.front ==Q.rear
队满:
1)设置标志位
2)设置计数器
3)少用一个单元:
队列实现
1.循环队列实现
循环队列的存储结构: 存储空间的基地址(base)、头指针(int型 front)、尾指针(int型 ,rear)
算法
初始化
1.为队列分配长度为MAXQSIZE的数组空间,base指针指向数组空间的首地址
2.front和rear置为0,表示队空
求队列长度
对于循环队列,front和rear差值可能小于0,所以求队列长度时,需要将两者差值 + MAXQSIZE 再 % MAXQSIZE.
入队
1.判断队是否已满,若队满则返回error
2.将新元素插入队尾
3.队尾指针加1
出队
1.判断是否队空,队空则返回error
2.保存队头元素
3.队头指针加1
取队头元素
若队非空时,返回队头元素,队头指针保持不变
#include <iostream>
using namespace std;
#define qElemType int
#define MAXSIZE 5
//结构体描述循环队列
typedef struct sqList{
qElemType *base; //存储空间的基地址
int front; //头指针
int rear; //尾指针
};
//初始化
void initSqQueue( sqList &Q ){
Q.base = new qElemType[MAXSIZE];
Q.front = Q.rear = 0;
}
int isFull( sqList Q ){
return Q.front == (Q.rear + 1) % MAXSIZE;//少用一个单元
}
//入队
void enQueue( sqList &Q, qElemType val ){
if( isFull( Q ) ){
cout << "队满了,无法入队!" << endl;
return;
}
Q.base[ Q.rear ] = val;
Q.rear = (Q.rear + 1) % MAXSIZE;
}
void queueTraverse( sqList Q ){
for( int i = Q.front; i % MAXSIZE != Q.rear ; i = (i + 1) % MAXSIZE )
cout << Q.base[i] << endl;
}
//出队
void deQueue( sqList &Q, qElemType &val ){
val = Q.base[ Q.front ];
Q.front= (Q.front+ 1) % MAXSIZE;
}
//取队头元素
qElemType getHead(sqQueue Q){
if( Q.front != Q.rear){
return Q.base[Q.front];
}
}
2.链队实现
链队为什么需要队头指针和队尾指针:因为队列需要从队头和队尾进行操作,因此需要头结点和队头队尾指针。跟链栈不同,栈只需在栈顶操作
链队的存储结构: 结点结构:数据域 、next域
队列结构:队头指针、队尾指针
算法
初始化
1.生成头结点,队头队尾指针指向头结点
2.头结点指针域置空
入队
1.为入队元素创建新的结点,指针p指向该结点
2.将新结点的数据域赋值为e
3.将新结点插入到队列中
4.将队尾指针改为p
出队
1.判断是否队空,队空则返回error
2.临时保存队头指针
3.修改队头指针指针域使其指向下一个结点
4.判断出队元素是否为最后一个元素,若是,将队尾指针重新赋值,指向头结点
5.释放临时保存的队头指针
取队头元素
若队列非空,返回队头元素的值,队头指针保持不变
实现代码
#include <iostream>
using namespace std;
#define qElemType int
//结构体:描述结点
typedef struct queueNode{
qElemType data;
queueNode *next;
} *queueNodePtr ;
//结构体:描述链队
typedef struct linkQueue{
queueNodePtr front;
queueNodePtr rear;
};
//初始化
void initLinkQueue( linkQueue &Q ){
queueNodePtr newNode = new queueNode;//生成头结点
Q.front = Q.rear = newNode;
newNode->next = NULL;
}
//入队
void enQueue( linkQueue &Q, qElemType val ){
queueNodePtr newNode = new queueNode;
newNode->data = val;
newNode->next = NULL;
Q.rear->next = newNode;//将新结点插入队尾
Q.rear = newNode; //修改队尾指针
}
void queueTraverse( linkQueue Q ){
Q.front = Q.front->next;
while( Q.front ){
cout << Q.front->data << endl;
Q.front = Q.front->next;
}
}
int isEmptyLinkQueue( linkQueue Q ){
return Q.front == Q.rear;
}
//出队
void deQueue( linkQueue &Q, qElemType &val ){
if( isEmptyLinkQueue(Q) ){
cout << "当前队列为空,无法出队" << endl;
return;
}
queueNodePtr p = Q.front->next;//指向队头元素
e = p->data;
Q.front->next = p->next;
if( !Q.front->next ) //最后一个元素被删,队尾指针指向头结点
Q.rear = Q.front;
delete p; //释放对头元素的空间
}
//取队头元素
qElemType getHead(linkQueue Q){
if(Q.front != Q.rear){
return Q.front->next->data;
}
}
应用案例:模拟银行排队
1.顾客进入银行的间隔时间:介于1-5的随机时间间隔
2.顾客排队接受银行服务的时间:1-30的随机时间间隔
3.每个银行窗口对应一个队列
4.队伍选取规则:顾客进入银行后,自动排到人数最少的队伍里
0:表示客户到达类型
变量定义:
1.事件链表: 有序的事件列表,保存到达或离开的事件
2.银行窗口队列:队列的结点结构包括:客户到达时间、接受服务时间
事件链表orderList
#include <iostream>
using namespace std;
typedef struct orderListNode{
int occurTime; //事件发生时间:即客户到达或者离开时间
int type; //事件的类型
orderListNode *next;//指针域
} *orderList ;
void initOrderList( orderList &L ){
L = new orderListNode;//定义头结点
L->next = NULL;
}
int isEmptyOrderList(orderList L){
return L->next == NULL;
}
//出队、输出事件队列的信息。没有删除
void delFirstOrderList( orderList L, int &occurTime, int &type ){
if( isEmptyOrderList(L) ){
cout << "当前有序表为空!" << endl;
return;
}
occurTime = L->next->occurTime;
type = L->next->type;
L->next = L->next->next;
}
//入队,插入顾客到达或者离开的信息
void insertOrderList( orderList L, int occurTime, int type ){
orderList newNode = new orderListNode;
newNode->occurTime = occurTime;
newNode->type = type;
orderList ptr, prePtr = L;
ptr = L->next;
//有序插入事件队列的元素
while( ptr && ptr->occurTime < newNode->occurTime ){
ptr = ptr->next;
prePtr = prePtr->next;
}
newNode->next = prePtr->next;
prePtr->next = newNode;
}
void orderListTraverse( orderList L ){
L = L->next;
while( L ){
cout << "occurTime is : " << L->occurTime << " "
<< "type is : " << L->type << endl;
L = L->next;
}
}
银行窗口队列 linkQueue
#include "orderList(1).cpp"
//结构体描述银行窗口结点结构
typedef struct queueNode{
int arrivalTime; //顾客到达银行的时间
int duringTime; //顾客接受窗口服务花费的时间
queueNode *next;
} *queueNodePtr ;
//结构体描述银行窗口队列
typedef struct linkQueue{
queueNodePtr front;
queueNodePtr rear;
int length;
};
void initLinkQueue( linkQueue &Q ){
queueNodePtr newNode = new queueNode;//生成头结点、队头队尾元素均指向头结点
Q.front = Q.rear = newNode;
newNode->next = NULL;
Q.length = 0;
}
void enQueue( linkQueue &Q, int arrivalTime, int duringTIme ){
queueNodePtr newNode = new queueNode;
newNode->arrivalTime = arrivalTime;
newNode->duringTime = duringTIme;
newNode->next = NULL;//将新结点插入
Q.rear->next = newNode;
Q.rear = newNode;//修改队尾指针
Q.length++;
}
void queueTraverse( linkQueue Q ){
Q.front = Q.front->next;
while( Q.front ){
cout << "arrivalTime is : " << Q.front->arrivalTime
<< " " << "duringTime is : " << Q.front->duringTime << endl;
Q.front = Q.front->next;
}
cout << "队列的长度是: " << Q.length << endl;
}
int isEmptyLinkQueue( linkQueue Q ){
return Q.front == Q.rear;
}
int getQueueFrontDuringTime(linkQueue Q){
return Q.front->next->duringTime;
}
void deQueue( linkQueue &Q, int &arrivalTime, int &duringTime ){
if( isEmptyLinkQueue(Q) ){
cout << "当前队列为空,无法出队" << endl;
return;
}
arrivalTime = Q.front->next->arrivalTime;
duringTime = Q.front->next->duringTime;
queueNodePtr temp = Q.front->next;
Q.front->next = Q.front->next->next;//删除元素
if( !Q.front->next )//最后元素被删除
Q.rear = Q.front;
delete temp;
Q.length--;
}
主要部分
#include "linkQueue.cpp"
#include <stdlib.h>
#include <time.h>
#define WINDOWS_NUM 5 //工作窗口
#define BANK_OPEN 0 //开门时间
#define BANK_CLOSE 20 //关门时间
#define ARRIVAL_INTER_TIME 5 //客户达到时间范围
#define DURING_TIME 30 //客户服务时间范围
orderList evList; //事件链表
linkQueue queueWindow[WINDOWS_NUM + 1]; //银行窗口,从1开始,5个窗口
int humanNum; //客户数量
double spentTime; //花费总时间
int occurTime, type; //事件时间和类型
void initBank(){
// ev表示有序的事件列表,用来保存发生的到达或离开的事件
initOrderList( evList );
for( int i = 1; i <= WINDOWS_NUM; i++ ) //银行窗口初始化
initLinkQueue(queueWindow[i]);
humanNum = spentTime = 0;
srand(time(0));//种下种子
}
int generateRand( int interTime ){
return rand() % interTime + 1;
}
int findMinLengthQueue( linkQueue queueWindow[] ){
int minIndex = 1, minLength = queueWindow[1].length;
for( int i = 2; i <= WINDOWS_NUM; i++ ){
if( queueWindow[i].length < minLength ){
minLength = queueWindow[i].length;
minIndex = i;
}
}
return minIndex;
}
void customerArrive(){
humanNum++;
// 产生下一个顾客到达的时间间隔
int newCustomerInterTime = generateRand( ARRIVAL_INTER_TIME );
// 产生一个新顾客到达的事件,并插入到事件列表里
insertOrderList( evList, occurTime + newCustomerInterTime, 0 );
int minLengthQueue = findMinLengthQueue( queueWindow );
int newCustomerServiceTime = generateRand( DURING_TIME );
enQueue( queueWindow[minLengthQueue], occurTime, newCustomerServiceTime );
// 如果顾客在排队之前队列为空,则可以马上计算出顾客出队的时间
if( 1 == queueWindow[minLengthQueue].length ){
insertOrderList( evList, occurTime + newCustomerServiceTime, minLengthQueue );
}
}
void customerLeave(){
int customerLeaveIndex = type;
int arrivalTime, duringTime;
deQueue( queueWindow[customerLeaveIndex], arrivalTime, duringTime );
spentTime += occurTime - arrivalTime;
cout << "该顾客所花费的时间是: " << occurTime - arrivalTime << endl << endl << endl;
//如果顾客排队之前队列不为空,需要计算离开时间
if( !isEmptyLinkQueue(queueWindow[customerLeaveIndex]) )
insertOrderList(evList, occurTime + getQueueFrontDuringTime(queueWindow[customerLeaveIndex]), customerLeaveIndex);
//排队情况下的离开时间计算
}
void bankOpen(){
insertOrderList( evList, BANK_OPEN, 0 );
while( !isEmptyOrderList(evList) ){
delFirstOrderList(evList, occurTime, type);
if( 0 == type ){
/* TODO (#1#): 这是一个到达事件
*/
if( occurTime <= BANK_CLOSE ){
cout << "===============有一名新的顾客到达!===============" << endl;
cout << "他的到达时间是: " << occurTime << endl << endl << endl;
customerArrive();
}
}
else{
cout << "当前有一个顾客从队伍 " << type << "离开" << endl;
/* TODO (#1#): 这是一个顾客离开的事件 */
customerLeave();
}
}
}
int main(){
initBank();
bankOpen();
cout << "总共有" << humanNum << "位顾客" << endl;
cout << "总共花费了" << spentTime << "的时间" << endl;
cout << "平均花费的时间是:" << spentTime / humanNum << endl;
}