队列的基本概念:
队列(Queue),和栈一样也是一种操作受限的线性表,只允许在一端进行插入,而在表的另一端进行删除。
特点:“先进先出”(First In First Out,FIFO)。
队头(Front):允许删除的一端,又称为队首。
队尾(Rear):允许插入的一端。
空队:不含任何元素的空表。
队列的应用:比如生活中排队买东西,先排队的先购买,平时我们用微信聊天,用键盘进行各种数字的输入,到聊天框中输出,也是队列的应用。
队列的基本操作:
InitQueue(&Q):队列初始化,构造一个空队列Q。
QueueEmpty(Q):判队列空,若队列Q为空返回true,否则返回false。
EnQueue(&Q,x):队列入队,若队列未满,将x加入,使之成为新的队尾。
DeQueue(&Q,&x):出队,若队列为空,删除堆头元素,并用x返回
GetHead(Q,&x) :读取堆头元素,若队列Q非空,则将队头元素赋值给x。
定义队列顺序存储类型
#define MaxSize 50
typedef struct{
int data[MaxSize];//用静态数组存放队列元素
int front,rear;//队头指针和队尾指针
}SqQueue;
队列初始化
void InitQueue(SqQueue &Q){
Q.front=Q.rear=0;
}
当Q.front=Q.rear=0;说明这个队列没有元素,是一个空队。根据图示
当Q.front再次和Q.rear相等,即Q.rear==MaxSize;是一个“假溢出”,其实队列中还有元素没有出队,但给人一种队列已经空的假象,因此Q.rear==MaxSize;并不能作为队列满的条件。
因此我们引出了循环队列
循环队列
循环队列将顺序队列臆造为一个环状的空间。
初始时:Q.front=Q.rear=0;
队首指针进1:Q.front=(Q.front+1)%MaxSize;
队尾指针进1:Q.rear=(Q.rear+1)%MaxSize;
队列长度:(Q.rear+MaxSize-Q.front)%MaxSize;
队满条件:(Q.rear-Q.front)%MaxSize==Q.front;
队空条件:Q.rear==Q.front;
队列中元素的个数:(Q.rear+MaxSize-Q.front)%MaxSize;
初始化
void InitQueue(SqQueue &Q){
Q.front=Q.rear=0;
}
判断队列是否为空
bool isEmpty(SqQueue Q){
if(Q.front==Q.rear)
return true;
else
return false;
}
入队
bool EnQueue(SqQueue &Q,int x){
if((Q.rear-Q.front)&MaxSize==Q.front))//队满报错
return false;
Q.data[Q.rear]=x;
Q.rear=(Q.rear+1)%MaxSize;//队尾指针加1取模
}
出队
bool DeQueue(SqQueue &Q,int &x){
if(Q.front==Q,rear) //队空报错
return false;
x=Q.data[Q.front];
Q.front=(Q.front+1)&MaxSize;//队头指针加1取模
}
在进行循环队列的判断,常常涉及到mod函数(取模),但是取模和取余有着一定的区别
例如:
a = -7;b = 4; a<0
第一步:
①进行求模运算时:c = [a/b] = -7 / 4 = -2(向负无穷方向舍入),
②进行求余运算时:c = [a/b] = -7 / 4 = -1(向0方向舍入);
第二步:
①求模时:r = a - c*b =-7 - (-2)*4 = 1,
②求余时:r = a - c*b = -7 - (-1)*4 =-3。
a = 7;b = 4;a>0(整数取余还是取模没有区别)
第一步:
①进行求模运算c = [a/b] = 7 / 4 = 1
②进行求余运算c = [a/b] = 7 / 4 = 1
第二步:
①求模时:r = a - c*b =7 - (1)*4 = 3,
②求余时:r = a - c*b = 7 - (1)*4 =3。
******C中提供的取模(%)是用来求两个整数相除的余数。关于取模运算例:A mod B,如果A小于或等于B,其结果是A。
队列的链式存储结构
队列的链式表示,实际上是一个同时带有队头指针和队尾指针的单链表。头指针指向队头结点,为指针指向队尾结点。
队列的链式存储类型
//强调结点
typedef struct LinkNode{//链式队列结点
int data;
struct LinkNode *next;
}LinkNode;
//强调链式队列
typedef struct{//链式队列
LinkNode *front,*rear;//队列的队头指针和队尾指针
}*LinkQueue;
初始化
//初始化
void InitQueue(LinkQueue &Q){
Q.front=Q.rear=(LinkNode *)malloc(sizeof(LinkNode));//为队列分配一个头结点
Q.front->next=NULL;//初始为空
}
判断队列是否为空
//判断队列是否为空
bool IsEmpty(LinkQueue Q){
if(Q.front==Q.rear)
return false;
else
return true;
}
入队操作
void EnQueue(LinkQueue &Q,int x){
LinkNode *s=(LinkNode *)malloc(sizeof(LinkNode));//创建新结点
s->data=x;
s->next=NULL;//插入到链尾
Q.rear->next=s;
Q.rear=s;
}
出队操作
bool DeQueue(LinkQueue &Q, int &x){
if(Q.front==Q.rear)//空队
return false;
LinkNode *p=Q.front->next;
x=p->data;
Q.front->next=p->next;
if(Q.rear=p)//如果p指针指向的结点和尾指针指向的是同一个结点
Q.rear=Q.front;//队列只有一个结点,删除后队列变空
free(p);
return true;
}
双端队列
双端队列是指允许两端都可以进行入队和出队操作。将队列的两端分别称为前端和后端。
输出受限的双端队列:允许进行插入和删除,但在另一端只允许插入的双端队列。
输入受限的双端队列:允许进行插入和删除,但在另一端只允许删除的双端队列。