队列(queue)使只允许在一段进行插入操作,而在另一端进行删除操作的线性表。
队列使一种先进先出的线性表,简称FIFO(first in first out)。允许插入的一端称为队尾,允许删除的一端称为队头。
顺序队列:
把顺序表的一端当作队头,另一端当作队尾,但是由于队列和顺序表的一些特点导致这样设计出的队列结构,出队和入队的操作总有一个的时间复杂度为O(n)。那如何在保证队列的特性下使得入队和出队的操作都降成O(1)那?
这个不难解决,就是在出队操作时,将指向队头的指针向队尾的方向移动。但这样就会狠浪费空间,为了解决上述的这些问题,就产生了我们熟悉的“循环队列”。
循环队列:头尾相接的顺序存储结构组成的队列。(解决了假溢出和入队、出队的时间复杂度不同时为0的问题)
但这样会也会产生一个问题,在一开始队头的位置和队尾的位置是相同,在队满之后两者还是相同的,这就使得判断条件产生了二义性。解决这个问题的方法有3种:
(1)因为队空是由于进行了出队的操作导致的,而队满则是由入队操作导致的,可以设置一个标志位初始化尾出队状态。当队头指针等于队尾指针时,查看标志位以判断是队满还是对空。
(2)设置一个计数器,记录队列的长度。
(3)队中少存储一个空间。一般使用的方法。
当使用方法3时,队空的判断条件为: front(队头)=rear(队尾)
队满的判断条件为:front=(rear+1)%MAXSIZE
求队长度的条件为:length=(rear-front+MAXSIZE)%MAXSIZE
//数组实现循环队列
/*
(1)初始化front=rear=0
(2)队空
(3)队满
(4)入队
(5)出队
(6)取队头元素
*/
#define MAXSIZE 5
typedef int ElemType;
typedef struct _Queue
{
ElemType data[MAXSIZE];
int front;
int rear;
}Queue;
//初始化队列
void initQueue(Queue* queue);
//队空
bool orderQueueEmpty(Queue queue);
//队满
bool orderQueueFill(Queue queue);
//入队
bool orderQueuePush(Queue* queue,ElemType value);
//出队
bool orderQueuePop(Queue* queue);
//取队头元素
ElemType getQueueTop(Queue queue);
#include "orderQueue.h"
void initQueue(Queue* queue)
{
queue->front = 0;
queue->rear = 0;
}
bool orderQueueEmpty(Queue queue)
{
return (queue.front == queue.rear) ? true : false;
}
bool orderQueueFill(Queue queue)
{
bool bRet = false;
if (queue.front == (queue.rear + 1) % MAXSIZE)
{
bRet = true;
}
return bRet;
}
bool orderQueuePush(Queue* queue, ElemType value)
{
bool bRet = false;
if (!orderQueueFill(*queue))
{
queue->data[queue->rear] = value;
queue->rear = (queue->rear + 1) % MAXSIZE;
}
return bRet;
}
bool orderQueuePop(Queue* queue)
{
bool bRet = false;
if (!orderQueueEmpty(*queue))
{
queue->front = (queue->front + 1) % MAXSIZE;
}
return bRet;
}
ElemType getQueueTop(Queue queue)
{
ElemType t=_INTEGRAL_MAX_BITS;
if (!orderQueueEmpty(queue))
{
t=queue.data[queue.front];
}
return t;
}