目录
队列
队列的顺序存储结构
队列队列,顾名思义就是平常我们排队的时候的一种结构,下面JV带你详细看看它的定义。
队列(queue)是只允许在一端进行插入操作(队尾),而在另一端进行删除操作(队头)的线性表。它先进先出(First In First Out),就像排队一样。
为什么删除操作是O(n)呢?因为删除队头之后会有一次元素的移动。那么我们能不能想想办法,优化一下,让删除操作简单一些。
我们说执行删除操作的一端称为队头,但没有要求队头一定在下标为0的地方,因此,我们可以引入两个指针分别指向队列的头尾,这样就不用在删除操作时移动元素,而是移动指针即可,这样时间复杂度就从O(n)变成了O(1)。
OK,现在我们有了两个指针:front和rear,分别指向队头和队尾。
那么,我们怎么判断这个队列是空是满呢?有人会说,很简单呀,既然front指向队头,rear指向队尾,那么只要front=rear,队列就满了呗。这位同学你太天真啦,看看下图。
你看,这两种情况不都满足front=rear么,但是一个队空一个队满,那么到底怎么判断队列的空还是满呢?
其实有两种方法:
1、我们发现这两种情况的同一点在于front=rear,不同点在于一个全空,一个全有。那么我们可以找个队列里最看的顺眼的地方,立个flag标记一下,当front=rear时,我们对flag的地方进行判断,若flag为空,则队列全为空,反之则全为满。
2、修改判断条件,不再让front=rear来判断空满,我们可以重新定义:front=rear时,队空,当还有一个元素空间为空时,队满。
就是这种亚子滴~
我们来讨论讨论为什么这样做,而不是直接定义front=rear+1或front=rear-1呢? 因为用上了指针,我们当然希望队列能够更加具有效率,为了尽可能的利用数组的空间,rear可能出现在front的前面或者后面,而你告诉计算机的时候,又不能有歧义,于是我们定义当还有一个元素空间为空时,队满。
这思路怎么告诉计算机呢?设队列的最大尺寸为QueueSize,那么队列满的条件应该是(rear+1)%QueueSize==front,计算队列长度的公式为:(rear-front+QueueSize)%QueueSize(因为有一个空的,所以它不能算进长度,也就是说,这个公式算的是队列的真实使用长度) 你可以待入前面的队列里,实践一下。
ok,接下来我们就可以开始写代码啦
typedef int QElemType;
typedef struct
{
QElemType data[MAXSIZE];
int front;
int rear;
}SqQueue;
//初始化一个空队列
Status InitQueue(SqQueue *Q)
{
Q->front=0;
Q->rear=0;
return OK;
}
//返回队列的元素个数,即当前长度
int QueueLength(SqQueue Q)
{
return (Q.rear-Q.front+MAXSIZE)%MAXSIZE;
}
//入队操作
Status EnQueue(SqQueue *Q,QElemType e)
{
if((Q->rear+1)%MAXSIZE==Q->front)
return ERROR; //判断队列是否满了
Q->data[Q->rear]=e; //赋值
Q->rear=(Q->rear+1)%MAXSIZE; //指向下一个
return OK;
}
//出队操作,用e返回其值
Status DeQueue(SqQueue *Q,QElemType *e)
{
if((Q->rear+1)%MAXSIZE==Q->front)
return ERROR; //判断队列是否满了
*e=Q->data[Q->front];//将队头元素赋值给e
Q->front=(Q->front+1)%MAXSIZE //front指针向后移动一位
//若到最后则转到数组头部
return OK;
}
队列的链式存储结构
队列的链式存储结构,其实就是线性表的单链表,只是只能尾进头出而已。
空队列时,front和rear都指向头结点。
typedef int QElemType;
typedef struct QNode //结点结构
{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct
{
QueuePtr front,rear; //队头队尾指针
}LinkQueue;
入队操作
Status EnQueue(LinkQueue *Q,QElemType e)
{
QueuePtr s=(QueuePtr)malloc(sizeof(QNode))
if(!s)
exit(OVERFLOW);
s->data=e;
s->next=NULL;
Q->rear->next=s;
Q->rear=s;
return OK;
}
出队操作
//删除队头,并用e返回其值
Status DeQueue(LinkQueue *Q,QElemType *e)
{
QueuePtr p;
if(Q->front==Q->rear) //判断是否为空
return ERROR;
p=Q->front->next;
*e=p->data;
Q->front->next=p->next;
if(Q->rear==p)
Q->rear=Q->front;
free(p);
return OK;
}