目录
之前提到了栈,它是只能在一端插入删除的线性表。对照它,队列好比生活中的排队,总人数减少就是队头的人离开,总人数增多就是有新的人排在队尾。
一、基本概念
队列是一种限定存取位置的线性表。它只允许在一段出入,在另一端删除。
队头:允许删除的一端
队尾:允许插入的一端
空队列:不含任何元素的空表
基本操作:
- InitQueue(&Q):初始化队列,构造一个空队列Q
- QueueEmpty(Q):判队列空,若队列Q为返回true,否则返回false
- EnQueue(&Q,x):入队,若队列Q未满,将x加入,使之称为新的队尾
- DeQueue(&Q,&x):出队,若队列非空,则将队头元素赋值给x
- GetHead(Q,&x):读队头元素,若队列Q非空,则将队头元素赋值给x
(注意:队列操作首先,许多线性表的操作不能用,比如不能随便读取队列中间某个数据)
线性表分为顺序表和链表,栈有顺序栈和链栈,同样队列按照存储方式分为顺序队和链队
二、顺序队列
1.队列的顺序实现是在连续存储单元中存放队列的元素,并设置front指针指示队头,rear指示队尾
初始化队列令front=rear=0
每当添加一个元素,先将元素添加到reara所指的位置,再让队尾指针加1,因此rear指示了实际队尾位置的后一位置。,即下一个元素应当加入的位置。而队头指针front指向队头元素。
但这种设定存在问题。
上图中初始的空队,有Q.front==Q.rear==0成立,但队满时,不能最后用Q.rear==MaxSize作为队满的条件,上图中最后满足
Q.rear==MaxSize ,但是队列中仍有一个元素。这种现象被称为假溢出
为了解决上面的问题,又引入了循环队列。
2. 将顺序队列从逻辑上视为一个环。当队首指针Q.front=MaxSize-1后,在前进一个位置就自动到0
- 初始时: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
- 出队入队时:指针都按顺时针方向进1
队列顺序存储
#define MaxSize 100 //定义队列中元素的最大个数
typedef struct{
ElemType data[MaxSize]; //存放队列元素
int front,rear; //队头指针和队尾指针
}SqQueue;
队空条件:Q.front=Q.rear,
当入队元素速度远大于出队元素,队尾指针很快会赶上队首指针,则队满时也有Q.front=Q.rear
为了区分队空和队满有三种区分方式
(1)牺牲一个 单元来区分队空和队满:约定(队头指针在队尾指针的下一位置作为队满的标志)
队满条件:(Q.rear+1)%MaxSize==Q.front
队空条件:Q.front=Q.rear
队列中元素个数:(Q.rear-Q.front+MaxSize)%MaxSize
(2)类型中增设表示元素个数的数据成员。
队满条件:Q.size==0
队空条件:Q.size==MaxSize
(3)类型增设tag数据成员,以区分队满还是队空。tag为0,若因删除导致Q.front==Q.rear,则为队空;tag等于1,若因插入导致 Q.front==Q.rear,则为队满
3.循环队列的操作
(1)初始化
void InitQueue(SeQueue &Q){
Q.rear=Q.front=0; //初始化队首、队尾指针
}
(2)判队队空
bool isEmpty(SeQueue &Q,ElemType x){
if(Q.rear==Q.front) //队空条件
return ftrue;
else
return false;
}
(3)入队
bool EnQueue(SeQueue &Q,ElemType x){
if((Q.rear+1)%MaxSize==Q.front) //队满
return false;
Q.data[Q.rear]=x;
Q.rear=(Q.rear+1)%MaxSize; //队尾指针+1取模
return true;
}
(4)出队
bool DeQueue(SeQueue &Q,ElemType x){
if(Q.rear=Q.front) //队空,报错
return false;
x=Q.data[Q.front]
Q.front=(Q.front+1)%MaxSize; //队头指针+1取模
return true;
}
三、链式队列
1.基本概念
队列的链式存储称为链队列。实际为一个同时带有队头指针和队尾指针的单链表。头指针指向队头结点,尾指针指向队尾结点。(不带头结点的如下图)
链式队:
typedef struct{ //链式队列结点
ElemType data;
struct LinkNode *next;
}LinkNode;
typedef struct{ //链式队列
LinkNode *front,*rear; //队列的队头和队尾指针
}LinkQueue;
当Q.front==NULL 且 Q.rear==NULL时,队列为空
出队时,首先判断队是否为空,若非空,则取出队头元素,将其从链表删除,并让Q.front指向下一个结点。
入队时,建立一个新结点,将新结点插入到链表的尾部,并改让Q.rear指向这个新插入的结点。
不带头结点的链式队列在操作上麻烦,所以设计为带头结点的使得插入删除操作统一。
2.基本操作
(1)初始化
void InitQueue(LinkQueue &Q){
Q.front=Q.rear=(LinkNode*)malloc(sizeof(LinkNode)); //建立头结点
Q.front->next=NULL; //初始为空
}
(2)判队空
bool IsEmpty(LinkQueue Q){
if(Q.front==Q.rear) return true;
else return false;
}
(3)入队
void EnQueue(LinkQueue){
LinkNode *s=(LinkNode *)malloc(sizeof(LinkNode));
s->data=x; //创建新结点,插入到链尾
s->next=NULL;
Q.rear->next=s;
Q.rear=s;
}
(4)出队
bool DeQueue(LinkQueue &Q,ElemType &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)
Q.rear=Q.front; //若原队列中只有一个结点,删除后为空
free(p);
return true;
}
四、双端队列
双端队列是指允许两端都可以进行入队和出队操作的队列,其元素的逻辑结构仍是线性结构。将队列的两端分别称为前端和后端,两端都可以入队和出队
在双端队列出队时,无论前端还是后端出队,先出的元素排在后出的元素前面
由普通的双端队列又可改造为:
(1)输出受限的双端队列:允许在一端插入和删除,但在另一端只允许插入的双端队列
(2)输入受限的双端队列:允许在一端插入和删除,但在另一端只允许删除的双端队列
//收藏大佬代码,https://www.cnblogs.com/wychen5/p/10445264.html
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#define true 1
#define false 0
#define BUF_SIZE 8
typedef struct Queue
{
int * BUF;
int front;
int rear;
}QUEUE;
void initQueue(QUEUE *queue_q)
{
queue_q->BUF = (int *)malloc(sizeof(int)*BUF_SIZE);
if(queue_q->BUF != NULL) //队列内存分配成功
{
queue_q->front = queue_q->rear = 0; //初始化头尾指针
}
}
//判空
unsigned char isemptyQueue(QUEUE *queue_q)
{
if(queue_q->front == queue_q->rear)
{
return true;
}
else
return false;
}
//判满
unsigned char is_fullQueue(QUEUE *queue_q)
{
if((queue_q->rear +1)%BUF_SIZE == queue_q->front)
{
return true;
}else
return false;
}
//入队
void In_Queue(QUEUE *queue_q , int value)
{
if(is_fullQueue(queue_q) != true) //队列未满
{
queue_q->BUF[queue_q->rear] = value;
queue_q->rear = (queue_q->rear + 1)%BUF_SIZE ; //尾指针偏移
}
}
//出队
void out_Queue(QUEUE *queue_q , int *value)
{
if(isemptyQueue(queue_q) != true) //队列未空
{
*value = queue_q->BUF[queue_q->front];
queue_q->front = (queue_q->front + 1)%BUF_SIZE ;
}
}
void bianli_a(QUEUE *queue_q)
{
if(isemptyQueue(queue_q) != true)
{
int ret=queue_q->front;
while(ret != queue_q->rear)
{
printf("%d ",queue_q->BUF[ret]);
ret=(ret+1)%BUF_SIZE;
}
}
}
int main()
{
int val;
QUEUE m;
initQueue(&m);
In_Queue(&m,1);
In_Queue(&m,2);
In_Queue(&m,3);
bianli_a(&m);
printf("\n");
out_Queue(&m,&val);
bianli_a(&m);
return 0;
}