一.顺序队列的概念
1.队列是操作受限的线性表 :队列只能在队头和队尾进行入队出队的操作,不能在队中进行插入删除操作。
2.存储地址连续 :用一组地址连续的存储单元依次存放队头到队尾的元素。
3.具有队头队尾指针 :设置两个整形 变量front(队头指针)和rear(队尾指针)分别指示 队头和队尾元素,头指针始终指向 队头元素,尾指针始终指向 队尾元素的下一个位置,每当插入一个新的元素时,队尾指针加一。
4."先进先出,后进后出" :队列中元素遵循先进先出,后进后出的入队出队原则。
二.循环队列
1.常规顺序队列的局限性---"假溢出"现象
(1)常规顺序队列的特点
- 常规队列判断队满的条件为队尾指针rear加一 是否等于最大存储空间MAXQSIZE 。
- 常规队列队尾指针移动的代码:rear=rear+1。
(2)假溢出现象:
如下图第四种情况,根据队满的判断条件,此时已经不能再入队,但是仍有6个空间未被利用,这就是假溢出现象
2.循环队列---解决"假溢出"现象的方法
头尾指针以及队列元素之间的关系不变,只是对头尾指针的移动方式进行改变,使线性的存储空间可以进行循环操作
(1)循环队列与常规顺序队列的不同点
- (头)尾指针移动的方改变为:(front)rear=(rear(front)+1)%MAXSIZE
- 判断队满的条件改变为:(rear+1)%MAXSIZE=front,牺牲一个存储空间,即存储空间大小为m时,有m-1个元素就判断为队满。
我们只是在思维上将循环队列想象成一个环状,实际上他仍然是一个线性的存储结构,我们只是巧妙的运用了rear=(rear+1)%MAXSIZE这一指针移动方式,让指针可以来回往复的移动。
三.循环队列存储结构的创建
存储结构需要一个指针型变量 来存储基地址,两个整型指针指示队头队尾元素。
js
#define MAXQSIZE 100
typedef struct{
int *base;//存储队列基地址(序号为0的,存储空间的地址),相当于数组名
int front;//头指针,出队时,作为数组下标
int rear;//尾指针,入队时,作为数组下标
}SqQueue;
四.循环队列的基本操作
1. 初始化
(1)为队列分配大小为MAXQSIZE的数组空间,base指向数组空间的首地址
(2)将队头队尾指针置为0,表示队列为空
js
int InitSqQueue(SqQueue &Q){
Q.base=new QElemType[MAXQSIZE];//QElemType意为任意类型的数据,如int,float,double等
if(!Q.base) return 0;
Q.front=Q.rear=0;
return 1;
}
2. 求队列长度(队列中元素的个数)
对于非循环队列来说,队头指针与队尾指针的差值便是队列的长度。对于循环队列差值可能为负数,计算较为复杂。
js
int QueueLength(SqQueue &Q){
//返回队列中元素的个数,即队列的长度
return (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;
}
3. 入队
(1)判断是否队满,若队满则返回0。
(2)将新元素插入队尾。
(3)队尾指针加1。
js
int EnQueue(SqQueue &Q,QElemType e){
if(Q.rear+1%MAXQSIZE==Q.front) return 0;
Q.base[Q.rear]=e;//先赋值
Q.rear=(Q.rear+1)%MAXQSIZE;//再移动队尾指针
return 1;
}
4. 出队
(1)判断是否队空,若队空返回0。
(2)保存队头元素。
(3)队头指针加1.
js
int DeQueue(SqQueue &Q,,QElemType &e){
if(Q.rear==Q.front) return 0;
e=Q.base[Q.front];
Q.front=(Q.front+1)%MAXQSIZE;
return 1;
5. 取队头元素
当队列非空时,此操作返回当前队头元素的值,队头指针保持不变。
scss
int GetHead(SqQueue Q){
if(Q.rear!=Q.front)
return Q.base[Q.front];//返回队头元素的值,并没有将其删除,队头指针不变
}
五.案例实现:舞伴问题、医院挂号就诊
- 在代码的实现过程中,注意形参实参的类型是否一致
- 在形参中,带&的变量为引用,返回的是一个地址
- 将对象(地址)赋值给引用,而不是对象
舞伴问题
js
//舞伴问题
#include<iostream>
#include<new>
#define MAXQSIZE 100
using namespace std;
typedef struct{
char name[20];
char sex;
}Person;
typedef struct{
Person *base;//存储Person类型的数据,指针变量
int front;//队头指针
int rear;//队尾指针
}SqQueue;
int InitQueue(SqQueue &Q){
//队列的初始化
Q.base=new Person[MAXQSIZE];
if(!Q.base) return 0;
Q.front=Q.rear=0;
return 1;
}
int EnQueue(SqQueue &Q,Person e){
//入队,从队尾入队
if((Q.rear+1)%MAXQSIZE==Q.front) return 0;
Q.base[Q.rear]=e;
Q.rear=(Q.rear+1)%MAXQSIZE;
return 1;
}
int DeQueue(SqQueue &Q,Person &e){//返回引用(e)
//出队
if(Q.rear==Q.front) return 0;
e=Q.base[Q.front];
Q.front=(Q.front+1)%MAXQSIZE;
return 1;
}
int IsEmptyQueue(SqQueue &Q){
//判断队列是否为空
if(Q.front==Q.rear)
return 1;
else return 0;//0就是非空
}
Person GetHead(SqQueue Q){//获取队头元素时不使用取地址符是因为队头元素已经是一个指针类型,直接返回该指针即可。如果使用取地址符,则会返回指向指针的指针,不符合预期。
//获取队头元素
if(Q.rear!=Q.front)
return Q.base[Q.front];//base已经是个指针类型
}
void DancePartner(Person dancer[],int num){//将地址赋给新的数组
SqQueue Mdancers;
SqQueue Fdancers;
Person p;//创建Person类型的数据,接收Person型数组的值
InitQueue(Mdancers);//初始化男队列
InitQueue(Fdancers);//初始化女队列
for(int i=0;i<num;i++){
p=dancer[i];//dancer[i]包括舞者的姓名和性别
if(p.sex=='M') EnQueue(Mdancers,p);//p为Person类型,p.name则为char类型,所以不能传入p.name
else EnQueue(Fdancers,p);//否则进入女队列
}
cout<<"The dancing partners are:\n";
while(!IsEmptyQueue(Mdancers)&&!IsEmptyQueue(Fdancers)){
DeQueue(Mdancers,p);
cout<<p.name<<" ";//通过引用取值
DeQueue(Fdancers,p);
cout<<p.name<<"\n";
}
if(!IsEmptyQueue(Mdancers)){//如果男队列不为空
cout<<"The first man who can get a partner in the next round:\n"<<GetHead(Mdancers).name;//返回的是一个地址(队列头部的引用),尽量p=GetHead(Mdancers)
}
if(!IsEmptyQueue(Fdancers)){//如果女队列不为空
cout<<"The first woman who can get a partner in the next round:\n"<<GetHead(Fdancers).name;
}
}
int main(){
Person dancer[MAXQSIZE];//Person类型
int num;
cout<<"Please Total Number of People:\n";
cin>>num;
for(int i=0;i<num;i++){
cout<<"Please Enter Name:\n";
cin>>dancer[i].name;
cout<<"Please Enter sex(M/F):\n";
cin>>dancer[i].sex;
}
DancePartner(dancer,num);//数组名就是数组的地址(数组的引用是指针,指针存储地址,所以数组的引用是地址)指针型*用->访问,引用型&用.访问
}
医院挂号就诊
(1) 排队------输入排队病人的病历号,加入病人排队队列中;
(2) 就诊------病人排队队列中最前面的病人就诊,并将其从队列中删除;
(3) 查看队列------从队首到队尾依次列出所有的排队病人的病历号;
(4) 不再接受排队,队列中现有病人依次就诊。
(5) 下班------退出系统。
js
//医院挂号就诊
#include<iostream>
#define MAXQSIZE 100
using namespace std;
typedef struct{
int *base;
int front;
int rear;
}SqQueue;
//初始化队列
int InitQueue(SqQueue &Q){
Q.base=new int[MAXQSIZE];//分配基地址
if(!Q.base) return 0;
Q.front=Q.rear=0;
return 1;
}
//入队
void EnQueue(SqQueue &Q,long e){
if((Q.rear+1)%MAXQSIZE!=Q.front){
Q.base[Q.rear]=e;
Q.rear=(Q.rear+1)%MAXQSIZE;
}
else
cout<<"Queue is Full!\n";
}
//出队
void DeQueue(SqQueue &Q){
if(Q.rear!=Q.front){
//e=Q.base[Q.front];
cout<<Q.base[Q.front]<<" Entering the Treatment Room\n";
Q.front=(Q.front+1)%MAXQSIZE;
cout<<"The next visit is "<<Q.base[Q.front]<<"\n";
}
else cout<<"Queue is Empty!\n";
}
//求列表长度
int QueueLength(SqQueue &Q){
//返回队列中元素的个数,即队列的长度
return (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;
}
//判断队列是否为空
int QueueIsEmpty(SqQueue &Q){
if(Q.rear==Q.front) return 1;
else return 0;
}
//返回队头元素的值
int GetHead(SqQueue Q){
if(Q.rear!=Q.front)
return Q.base[Q.front];//返回队头元素的值,并没有将其删除,队头指针不变
}
//查看队列
void ShowQueue(SqQueue &Q){
if(Q.front==Q.rear) cout<<"Queue is Empty!\n";
else {
cout<<"The people in line are\n";
int f=Q.front;
int r=Q.rear;
while(f!=r){
cout<<Q.base[f]<<"\n";
f=(f+1)%MAXQSIZE;
}
}
}
//不再进行排队
void unChangeQueue(SqQueue &Q){
//SqQueue Q1;
//Q1=Q;
if(Q.front==Q.rear) cout<<"Queue is Empty!\n";
else{
while(Q.front!=Q.rear){
DeQueue(Q);
}
}
}
int main(){
cout<<"Experiment 3---dui lie gua hao---liuzun-202203180031\n";
SqQueue Q;
InitQueue(Q);//初始化队列
int flag=1;
int n;
long number;
while(flag){
cout<<"-----------------------------------------------------------------------------------------------------------------------\n";
cout<<" 1.line up "<<"2.Visit "<<"3.View queue "<<"4.No further visits "<<"5.Off duty \n";
cout<<"-----------------------------------------------------------------------------------------------------------------------\n";
cin>>n;
switch(n){
case 1:
cout<<"Enter Medical Record Number:";
cin>>number;
EnQueue(Q,number);
break;
case 2:
DeQueue(Q);
break;
case 3:
ShowQueue(Q);
break;
case 4:
unChangeQueue(Q);
break;
case 5:
if(Q.front!=Q.rear) cout<<"Please queue up patients for treatment tomorrow, the doctor will be off duty!\n";
else{
cout<<"There are no more patients left, we can finish work now!\n";
flag=0;
break;
}
}
}
}
六.栈与队列顺序存储结构的不同点
1.存储结构的不同点
(1)指针的不同点
- 栈的指针为top和base,为指针型变量,存放的是对象的引用(地址);队列的指针为front和rear,为非指针型变量,存放的是队列数组的下标(值)。
- 栈的栈底指针*base永远存放栈结构的基地址;队列的队尾指针rear不存放基地址,需要另设一个指针型变量存放基地址。
- 栈的指针的移动是通过地址的增加即top++,队列的指针移动是通过下标的变化即base.[front]的front的变化。
(2)数据的进出顺序不同
- 栈遵循"先进后出,后进先出"的原则;队列遵循"先进先出,后进后出的"的原则。
(3)操作受限的程度不同
- 栈只能在栈顶进行插入删除操作,从栈顶进,栈顶出。
- 队列可以在队头队尾两个位置进行操作,队尾进,队头出。
(4)遍历的不同
- 遍历的起始位置不同:栈只能从栈顶开始遍历,队列可以从队尾队头甚至根据下标从队中开始遍历。
- 遍历的速度不同:由于栈只能从栈顶开始遍历,所以栈的遍历的速度较低,而队列的遍历速度较高。