队列
一、队列的定义
队列和栈一样是一种操作受限的线性表,其特殊之处在于只允许在表的头部(即队头–front
)进行删除,而在表的尾部(即队尾–rear
)进行插入操作,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out
)线性表。队列的结构体如下图所示:
二、链队
链队是指采用链式存储结构来实现的队列,通常链队用单链表来表示,如下图所示,一个链队需要两个分别指示队头和队尾的指针才能确定。
代码描述:
typedef int ElemType;
typedef struct QNode
{
ElemType data;
struct QNode* next;
} QNode, *QNodePtr;
typedef struct LinkQueue
{
QNodePtr front,rear;
} LinkQueue, *LinkQueuePtr;
三、链队的基本操作
链队的初始化
算法步骤:
- 生成新的结点作为头结点,队头(
front
)和队尾(rear
)指针指向此结点 - 头结点的指针域为空
代码如下:
/* 初始化队列 */
LinkQueuePtr InitQueue()
{
LinkQueuePtr resultPtr = (LinkQueuePtr)malloc(sizeof(LinkQueue));
QNodePtr header = (QNodePtr)malloc(sizeof(QNode));
header->next = NULL;
resultPtr->front = header;
resultPtr->rear = header;
return resultPtr;
}
入队
由于链队是基于链表的队列,其结点为动态创建和动态删除,故不存在溢出等问题。
算法步骤:
- 为入队元素分配结点空间,用指针
tempNodePtr
指向 - 将新结点的数据域改为
tempElem
,指针域改为NULL
- 将新结点插入到队尾
- 修改队尾指针(
rear
)为tempNodePtr
如下图所示:
代码如下:
/* 入队操作 */
void EnQueue(LinkQueuePtr tempQueuePtr, ElemType tempElem)
{
QNodePtr tempNodePtr = (QNodePtr)malloc(sizeof(QNode));
tempNodePtr->data = tempElem;
tempNodePtr->next = NULL;
tempQueuePtr->rear->next = tempNodePtr;
tempQueuePtr->rear = tempNodePtr;
}
出队
链队在出队前需要判断队列是否为空,链队在出队后需要释放队头元素的所占空间
算法步骤:
- 判断队列是否为空,若为空则返回
-1
- 用
tempNodePtr
临时保存队头元素的空间,以备释放 - 用临时变量
DeValue
保存队头元素的数据域,以用于返回 - 修改头结点的指针域,指向下一个结点
- 判断出队元素是否为最后一个元素,若是,则将队尾指针指向头结点
- 释放原头元素的空间并将
tempNodePtr
指针置为NULL
如下图所示:
代码如下:
/* 出队操作 */
ElemType DeQueue(LinkQueuePtr tempQueuePtr)
{
ElemType DeValue;
QNodePtr tempNodePtr;
if(tempQueuePtr->front == tempQueuePtr->rear)
{
printf("队列为空!\n");
return -1;
}
tempNodePtr = tempQueuePtr->front->next;
DeValue = tempNodePtr->data;
tempQueuePtr->front->next = tempNodePtr->next;
if(tempQueuePtr->rear == tempNodePtr)
{
tempQueuePtr->rear = tempQueuePtr->front;
}
free(tempNodePtr);
tempNodePtr = NULL;
return DeValue;
}
打印队列
/* 打印队列 */
void OutPutQueue(LinkQueuePtr tempQueuePtr)
{
QNodePtr tempNodePtr = tempQueuePtr->front->next;
while (tempNodePtr)
{
printf("% d",tempNodePtr->data);
tempNodePtr = tempNodePtr->next;
}
printf("\n");
return ;
}
取队头元素
/* 取队头元素 */
ElemType GetHead(LinkQueuePtr tempQueuePtr)
{
if (tempQueuePtr->front != tempQueuePtr->rear)
{
return tempQueuePtr->front->next->data;
}
printf("队列为空!\n");
return -1;
}
清空队列
/* 清空队列 */
void ClearQueue(LinkQueuePtr tempQueuePtr)
{
if(tempQueuePtr->front == tempQueuePtr->rear)
{
printf("队列为空!\n");
return ;
}
while(tempQueuePtr->front->next)
{
DeQueue(tempQueuePtr);
}
}
返回队列长度
/* 返回队列长度 */
int QueueLength(LinkQueuePtr tempQueuePtr)
{
int length = 0;
if(tempQueuePtr->front == tempQueuePtr->rear)
{
printf("队列为空!\n");
return length;
}
QNodePtr tempNodePtr = tempQueuePtr->front->next;
while (tempNodePtr)
{
length++;
tempNodePtr = tempNodePtr->next;
}
return length;
}
完整代码
#include <stdio.h>
#include <malloc.h>
typedef int ElemType;
typedef struct QNode
{
ElemType data;
struct QNode* next;
} QNode, *QNodePtr;
typedef struct LinkQueue
{
QNodePtr front,rear;
} LinkQueue, *LinkQueuePtr;
/* 初始化队列 */
LinkQueuePtr InitQueue()
{
LinkQueuePtr resultPtr = (LinkQueuePtr)malloc(sizeof(LinkQueue));
QNodePtr header = (QNodePtr)malloc(sizeof(QNode));
header->next = NULL;
resultPtr->front = header;
resultPtr->rear = header;
return resultPtr;
}
/* 打印队列 */
void OutPutQueue(LinkQueuePtr tempQueuePtr)
{
QNodePtr tempNodePtr = tempQueuePtr->front->next;
while (tempNodePtr)
{
printf("% d",tempNodePtr->data);
tempNodePtr = tempNodePtr->next;
}
printf("\n");
return ;
}
/* 入队操作 */
void EnQueue(LinkQueuePtr tempQueuePtr, ElemType tempElem)
{
QNodePtr tempNodePtr = (QNodePtr)malloc(sizeof(QNode));
tempNodePtr->data = tempElem;
tempNodePtr->next = NULL;
tempQueuePtr->rear->next = tempNodePtr;
tempQueuePtr->rear = tempNodePtr;
}
/* 出队操作 */
ElemType DeQueue(LinkQueuePtr tempQueuePtr)
{
ElemType DeValue;
QNodePtr tempNodePtr;
if(tempQueuePtr->front == tempQueuePtr->rear)
{
printf("队列为空!\n");
return -1;
}
tempNodePtr = tempQueuePtr->front->next;
DeValue = tempNodePtr->data;
tempQueuePtr->front->next = tempNodePtr->next;
if(tempQueuePtr->rear == tempNodePtr)
{
tempQueuePtr->rear = tempQueuePtr->front;
}
free(tempNodePtr);
tempNodePtr = NULL;
return DeValue;
}
/* 取队头元素 */
ElemType GetHead(LinkQueuePtr tempQueuePtr)
{
if (tempQueuePtr->front != tempQueuePtr->rear)
{
return tempQueuePtr->front->next->data;
}
printf("队列为空!\n");
return -1;
}
/* 清空队列 */
void ClearQueue(LinkQueuePtr tempQueuePtr)
{
if(tempQueuePtr->front == tempQueuePtr->rear)
{
printf("队列为空!\n");
return ;
}
while(tempQueuePtr->front->next)
{
DeQueue(tempQueuePtr);
}
}
/* 返回队列长度 */
int QueueLength(LinkQueuePtr tempQueuePtr)
{
int length = 0;
if(tempQueuePtr->front == tempQueuePtr->rear)
{
printf("队列为空!\n");
return length;
}
QNodePtr tempNodePtr = tempQueuePtr->front->next;
while (tempNodePtr)
{
length++;
tempNodePtr = tempNodePtr->next;
}
return length;
}
/* 测试 */
void test()
{
LinkQueuePtr tempQueuePtr = InitQueue();
printf("--------入队测试--------\n");
EnQueue(tempQueuePtr,5);
EnQueue(tempQueuePtr,2);
EnQueue(tempQueuePtr,0);
OutPutQueue(tempQueuePtr);
printf("--------队列长度--------\n");
printf("队列长度为%d\n",QueueLength(tempQueuePtr));
printf("--------出队测试--------\n");
printf("出队的元素是%d.\n",DeQueue(tempQueuePtr));
printf("出队的元素是%d.\n",DeQueue(tempQueuePtr));
printf("出队的元素是%d.\n",DeQueue(tempQueuePtr));
printf("出队的元素是%d.\n",DeQueue(tempQueuePtr));
printf("--------入队测试--------\n");
EnQueue(tempQueuePtr,5);
EnQueue(tempQueuePtr,2);
EnQueue(tempQueuePtr,0);
OutPutQueue(tempQueuePtr);
printf("--------取队头元素--------\n");
printf("队头元素是%d.\n",GetHead(tempQueuePtr));
printf("--------清空队列--------\n");
ClearQueue(tempQueuePtr);
OutPutQueue(tempQueuePtr);
printf("--------队列长度--------\n");
printf("队列长度为%d\n",QueueLength(tempQueuePtr));
printf("--------取队头元素--------\n");
printf("队头元素是%d.\n",GetHead(tempQueuePtr));
}
int main()
{
test();
}
运行测试
--------入队测试--------
5 2 0
--------队列长度--------
队列长度为3
--------出队测试--------
出队的元素是5.
出队的元素是2.
出队的元素是0.
队列为空!
出队的元素是-1.
--------入队测试--------
5 2 0
--------取队头元素--------
队头元素是5.
--------清空队列--------
--------队列长度--------
队列为空!
队列长度为0
--------取队头元素--------
队列为空!
队头元素是-1.
四、循环队列
循环队列就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。 循环队列可以更简单防止伪溢出的发生,但队列大小是固定的。
代码描述:
typedef int ElemType;
typedef struct CircleQueue
{
ElemType data[MAXQSIZE];
int front;
int tail;
} CircleQueue,*CircleQueuePtr;
五、循环队列的基本操作
循环队列的初始化
算法步骤:
- 为队列分配一个最大容量为
MAXQSIZE
的数组空间 - 头指针(
front
)和尾指针(tail
)置为零,表示队列为空,返回resultPtr
代码如下:
/* 初始化队列 */
CircleQueuePtr InitQueue()
{
CircleQueuePtr resultPtr = (CircleQueuePtr)malloc(sizeof(CircleQueue));
resultPtr->front = 0;
resultPtr->tail = 0;
return resultPtr;
}
入队
算法步骤:
- 判断队列是否满,若满返回
- 将新元素插入队尾
- 队尾指针加
1
注意:
当队列为空时,有front = tail
,而当所有队列空间全占满时,也有front=tail
。为了区别这两种情况,规定循环队列最多只能有MAXQSIZE - 1
个队列元素,当循环队列中只剩下一个空存储单元时,队列就已经满了。因此,队列判空的条件是front = rear
,队列判满的条件是front =(rear + 1) % MAXQSIZE
。
代码如下:
/* 入队操作 */
void EnQueue(CircleQueuePtr tempQueuePtr, ElemType tempValue)
{
if((tempQueuePtr->tail+1) % MAXQSIZE == tempQueuePtr->front)
{
printf("队列已满!\n");
return ;
}
tempQueuePtr->data[tempQueuePtr->tail % MAXQSIZE] = tempValue;
tempQueuePtr->tail++;
}
出队操作
算法步骤:
- 判断队列是否为空,为空则返回
-1
- 保存队头元素
- 队头指针加
1
/* 出队操作 */
ElemType DeQueue(CircleQueuePtr tempQueuePtr)
{
ElemType DeValue;
if (tempQueuePtr->front == tempQueuePtr->tail)
{
printf("队列为空!\n");
return -1;
}
DeValue = tempQueuePtr->data[tempQueuePtr->front % MAXQSIZE];
tempQueuePtr->front++;
return DeValue;
}
求队列长度
对于非循环队列,尾指针和和头指针之差便是队列长度,但对于循环队列而言,差值可能为负值,因此需要将差值加上MAXQSIZE
,然后对MAXQSIZE
取余。
代码如下:
int QueueLength(CircleQueuePtr tempQueuePtr)
{
return (tempQueuePtr->tail - tempQueuePtr->front + MAXQSIZE) % MAXQSIZE;
}
取循环队列的队头元素
代码如下:
/* 取队头元素 */
ElemType GetHead(CircleQueuePtr tempQueuePtr)
{
if(tempQueuePtr->front == tempQueuePtr->tail)
{
printf("队列为空!\n");
return -1;
}
return tempQueuePtr->data[tempQueuePtr->front];
}
完整代码
#include <stdio.h>
#include <malloc.h>
#define MAXQSIZE 5
typedef int ElemType;
typedef struct CircleQueue
{
ElemType data[MAXQSIZE];
int front;
int tail;
} CircleQueue,*CircleQueuePtr;
/* 初始化队列 */
CircleQueuePtr InitQueue()
{
CircleQueuePtr resultPtr = (CircleQueuePtr)malloc(sizeof(CircleQueue));
resultPtr->front = 0;
resultPtr->tail = 0;
return resultPtr;
}
/* 入队操作 */
void EnQueue(CircleQueuePtr tempQueuePtr, ElemType tempValue)
{
if((tempQueuePtr->tail+1) % MAXQSIZE == tempQueuePtr->front)
{
printf("队列已满!\n");
return ;
}
tempQueuePtr->data[tempQueuePtr->tail % MAXQSIZE] = tempValue;
tempQueuePtr->tail++;
}
/* 返回队列长度 */
int QueueLength(CircleQueuePtr tempQueuePtr)
{
return (tempQueuePtr->tail - tempQueuePtr->front + MAXQSIZE) % MAXQSIZE;
}
/* 出队操作 */
ElemType DeQueue(CircleQueuePtr tempQueuePtr)
{
ElemType DeValue;
if (tempQueuePtr->front == tempQueuePtr->tail)
{
printf("队列为空!\n");
return -1;
}
DeValue = tempQueuePtr->data[tempQueuePtr->front % MAXQSIZE];
tempQueuePtr->front++;
return DeValue;
}
/* 输出队列 */
void QueueTraverse(CircleQueuePtr tempQueuePtr)
{
if (tempQueuePtr->front == tempQueuePtr->tail)
{
printf("队列为空!\n");
return ;
}
printf("队列中的元素为:");
for(int i = tempQueuePtr->front; i < tempQueuePtr->tail; ++i)
{
printf("% d",tempQueuePtr->data[i % MAXQSIZE]);
}
printf("\n");
}
/* 取队头元素 */
ElemType GetHead(CircleQueuePtr tempQueuePtr)
{
if(tempQueuePtr->front == tempQueuePtr->tail)
{
printf("队列为空!\n");
return -1;
}
return tempQueuePtr->data[tempQueuePtr->front];
}
/* 测试 */
void test()
{
CircleQueuePtr tempQueuePtr = InitQueue();
printf("--------入队操作--------\n");
for(int i = 1; i < 5; ++i)
{
EnQueue(tempQueuePtr,i);
}
QueueTraverse(tempQueuePtr);
printf("当前队列长度是%d\n",QueueLength(tempQueuePtr));
printf("--------出队操作--------\n");
for(int i = 0; i < 6; ++i)
{
printf("出队元素是%d\n",DeQueue(tempQueuePtr));
}
printf("当前队列长度是%d\n",QueueLength(tempQueuePtr));
printf("--------入队操作--------\n");
for (int i = 5; i < 9; ++i)
{
EnQueue(tempQueuePtr,i);
}
QueueTraverse(tempQueuePtr);
printf("当前队头元素是%d\n",GetHead(tempQueuePtr));
}
int main()
{
test();
}
运行测试
--------入队操作--------
队列中的元素为: 1 2 3 4
当前队列长度是4
--------出队操作--------
出队元素是1
出队元素是2
出队元素是3
出队元素是4
队列为空!
出队元素是-1
队列为空!
出队元素是-1
当前队列长度是0
--------入队操作--------
队列中的元素为: 5 6 7 8
当前队头元素是5
六、总结
- 队列在日常生活中使用广泛,比如排队买 XXX、医院的挂号系统等,采用的都是队列的结构。
- 链式队列可以实现一个无限排队的无界队列,但是极有可能导致排队过多,请求被处理的时效非常长,所以针对响应时间要求高的系统,链式队列是不适用的
- 顺序队列是有界的,当请求入队使得队列满了后,后续请求都会被拒绝,比较适合对响应时间要求高的系统。