概述
队列(Queue)是一种操作受限的线性表,它只允许在表的一端进行元素插入(入队
),而在另一端进行元素删除(出队
)。允许插入的一端称为队尾(rear),允许删除的一端称为队头(front)
从上面队列示意图可以看出队列具有先进先出
的特性,因此又称队列为先进先出表(First In First Out,FIFO表)
一、顺序队列:队列的顺序存储结构称为顺序队列。
队列的顺序存储也是利用一块连续的存储单元存放队列中的元素,实际上是一个受限的线性表。因为队头和队尾的位置是变化的,因此需要设置两个指针front和rear表示队头和队尾在表中的位置。顺序队列的类型可以定义如下:
#define QueueSize 100
typedef int DataType;
typedef struct
{
DataType data[QueueSize];
int front, rear;
}SeqQueue;
基本运算
/*
初始化队列
*/
void InitQueue(SeqQueue *q){
q->front = 0;
q->rear = 0;
}
/*
入队
*/
void EnQueue(SeqQueue *q,int x){
q->data[q->rear] = x; //入队时将新元素插入队尾rear的位置
q->rear = q->rear + 1; //在将队尾加1
}
/*
出队
*/
DataType DeQueue(SeqQueue *q){
DataType x = q->data[q->front]; //出队时记录删除的元素
q->front = q->front + 1; //将队头加1
}
/*
空判断
*/
int QueueEmpty(SeqQueue *q){
//当头尾指针相等时队列为空
return q->front == q->rear;
}
下面是一个入队出队的操作例子:
当队列处于上图 (f) 状态时,如果再插入新元素就会产生上溢,而出队时空出的一些存储单元无法使用。如果队列的存储空间定义的太大,会产生存储空间的浪费。
为了充分利用数组空间,克服上溢,可将数据空间想象为一个环状空间,如下图:
二、循环队列:环状数组表示的队列
在循环队列中进行入队、出队时,头尾指针仍然需要加1,只不过当头尾指针指向数组上界(QueueSize - 1) 时,如果按正常加1运算,数组会产生越界溢出,因此需要判断加1后是否超过数组上届,若是则是其指向数组下届0。用i表示Q.front或Q.rear。则这种加1运算可描述为:
if (i + 1 == QueueSize) // i 表示 Q.rear 或 Q.front
i = 0;
else
i = i + 1;
利用求余(%)运算符将上述操作简化
i = (i + 1) % QueueSize;
循环队列的顺序存储类型定义如下:
#define QueueSize 10
typedef int DataType;
typedef struct
{
DataType data[QueueSize];
int front, rear;
}CirQueue;
基本方法如下:
/*
初始化空队列
*/
void InitQueue(CirQueue *q){
q->front = 0;
q->rear = 0;
}
/*
判队空
*/
int QueueEmpty(CirQueue *q){
//当头尾指针相等时队列为空
return q->front == q->rear;
}
/*
判队满
少用一个元素空间,约定入队前,测试尾指针在循环意义下加1是否等于头指针
若相等则队列满,即尾指针rear指向单元格始终为空(相当于尾指针占用一个空间)
*/
int QueueFull(CirQueue *Q){
return (Q->rear + 1) % QueueSize == Q->front;
}
/*
入队列
*/
void EnQuque(CirQueue *Q,DataType x){
printf("添加x = %dn",x);
if (QueueFull(Q))
{
printf("队列已满n");
}
else
{
Q->data[Q->rear] = x;
Q->rear = (Q->rear+1) % QueueSize; //循环意义下的加1
}
}
/*
获取队头元素
*/
DataType GetFront(CirQueue *Q){
if (QueueEmpty(Q))
{
printf("队列为空n");
exit(0);
}
else{
return Q->data[Q->front];
}
}
/*
出队列
*/
DataType DeQueue(CirQueue *Q){
DataType x;
if (QueueEmpty(Q))
{
printf("空队列n");
exit(0);
}
else
{
x = Q->data[Q->front]; //保存待删除的值
Q->front = (Q->front + 1) % QueueSize; //头指针加1
return x;
}
}
测试:
#include "stdio.h"
#include "CirQueue.c"
int main(){
CirQueue C,*Q;
Q = &C;
InitQueue(Q);
for (int i = 2; i < QueueSize; i++)
{
EnQueue(Q,i);
}
int front = GetFront(Q);
printf("入队之后头指针=%dn",front);
DeQueue(Q);
front = GetFront(Q);
printf("出队之后头指针=%dn",front);
return 0;
}
运行结果:
入队之后头指针=2
出队之后头指针=3
三、链队列
队列的链式存储结构称为链队列
。
链队列是一种限制在表头删除和表尾插入操作的单链表。为了方便入队和出队操作,一个链队列就由头指针和尾指针唯一确定。链队列的类型定义如下:
typedef int DataType;
typedef struct qnode{
DataType data; //数据域
struct qnode * next; //下个结点指针域
}QueueNode; //链队列结点类型
typedef struct{
QueueNode * front; //队头指针
QueueNode * rear; //队尾指针
}LinkQueue; //链队列类型
链队列一般也是不带头指针的,为了简化边界条件处理,在队头结点前附加一个头结点,并设头指针指向此结点。链队列结构示意图如下:
l链队列的基本运算:
/*
初始化队列
*/
void InitQueue(LinkQueue * Q){
Q->front = (QueueNode *)malloc(sizeof(QueueNode)); //申请头结点
Q->rear = Q->front; //队尾结点也指向头结点
Q->rear->next = NULL;
}
/*
队判空
*/
int QueueEmpty(LinkQueue * Q){
return Q->front == Q->rear; //头尾指针相等,队列为空
}
/*
入队列
*/
void EnQueue(LinkQueue * Q, DataType x){
QueueNode * p = (QueueNode *)malloc(sizeof(QueueNode)); //申请新结点
p->data = x;
p->next = NULL;
Q->rear->next = p; //*p链到原队尾结点之后
Q->rear = p; //队尾指针指向新的的队尾指针
}
/*
获取头元素
*/
DataType GetFront(LinkQueue * Q){
if (QueueEmpty(Q))
{
printf("空队列");
exit(0);
}
else
{
return Q->front->next->data; //返回队头元素
}
}
/*
出队列
*/
DataType DeQueue(LinkQueue * Q){
QueueNode * p;
if (QueueEmpty(Q))
{
printf("空队列");
exit(0);
}
else
{
DataType x;
p = Q->front; //p指向头结点
Q->front = Q->front->next; //头指针指向原队头结点
x = Q->front->data;
free(p); //删除释放原头结点
return x; //返回被删除结点值
}
}
测试:
int main(){
LinkQueue Q;
InitQueue(&Q);
EnQueue(&Q,10);
int front = GetFront(&Q);
printf("头元素=%dn",front);
EnQueue(&Q,100);
DeQueue(&Q);
front = GetFront(&Q);
printf("头元素=%dn",front);
return 0;
}
运行结果:
头元素=10
头元素=100
最后
以上就是苗条裙子为你收集整理的六、队列的全部内容,希望文章能够帮你解决六、队列所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复