什么是队列
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列(Queue):具有一定操作约束的线性表
插入和删除操作:只能在一端插入,而在另一端删除。
与栈相反,队列是一种先进先出(First In First Out,FIFO)的线性表。
与栈相同的是,队列也是一种重要的线性结构,实现一个队列同样需要顺序表或链表作为基础。
广泛应用:
1)我们的输入缓冲区接受键盘的输入就是按队列的形式输入和输出的
队列既可以用链表实现,也可以用顺序表实现。跟栈相反的是,栈一般我们用顺序表来实现,而队列我们常常用链表来实现,简称为链队列。
- 数据插入:入队列(AddQ)
- 数据删除:出队列(DeleteQ)
- 先来先服务
- 先进先出:FIFO
队列的顺序存储实现
队列的顺序存储结构通常由一个一维数组和一个记录队列头元素位置的变量front以及一个记录队列尾元素位置的变量rear组成。循环队列:
为什么队列的实现上更愿意用链式存储结构来存储?
先按照应有的思路来考虑下如何构造队列的顺序存储结构,然后发掘都遇到了什么麻烦。假设一个队列有n个元素,则顺序存储的队列需建立一个大于n的存储单元,并把队列的所有元素存储在数组的前n个单元,数组下标为0的一端则是队头。
按上面的算法,出队列的时间复杂度是O(n),效率大打折扣。
如果我们不去限制队头一定要在下标为0的位置,那么出队列的操作就不需要移动全体元素。
要解决假溢出的办法就是如果后面满了,就再从头开始,也就是头尾相接的循环。这就引入了循环队列的概念。
- 循环队列它的容量是固定的,并且它的队头和队尾指针都可以随着元素入出队列而发生改变,这样循环队列逻辑上就好像一个环形存储空间。(注意:在实际的内存当中,不可能有真正的环形存储区,我们只是用顺序表模拟出来的逻辑上的循环)。
- 循环队列的实现只需要灵活改变front和rear指针即可。
- 也就是让front或rear指针不断加1,即时超出了地址范围,也会自动从头开始。我们采取取模运算处理:
- (rear +1) % QueueSize
- (front +1) % QueueSize
- 取模就是取余数的意思,他取到的值永远不会大于除数。 - 定义一个循环队列
#define MAXSIZE 100
typedef struct
{
ElemType *base; //用于存放内存分配基地址
//这里你也可以用数组存放
int front;
int rear;
}
- 初始化一个循环队列
initQueue(cycleQueue *q)
{
q->base = (ElemType *)malloc(MAXSIZE * sizeof(ElemType));
if(!q->base)
exit(0);
q->front = q->rear = 0;
}
-
入队列(插入)操作
InsertQueue(cycleQueue *q, ElemType e)
{
if((q->rear+1)%MAXSIZE == q->front)
return; //队列已满
q->base[q->rear] =e;
q->rear = (q->rear+1) % MAXSIZE;
}
*出队列操作
DeleteQueue(cycleQueue *q ,ElemType *e)
{
if(q->front ==q->rear)
return ;
*e = q-base[q->front];
q->front =(q->front+1)%MAXSIZE;
}
队列的链式存储结构
将队头指针指向链队列的头结点,而队尾指针指向终端结点.(注:头结点不是必要的,但为了方便操作,我们加上了)。
- 创建一个队列:
创建一个队列要完成两个任务:一是在内存中创建一个头结点,二是将队列的头指针和尾指针都指向这个生成的头结点,因为此时是空队列。
initQueue(LinkQueue *q)
{
q->front = q->rear = (QueuePtr)malloc(sizeof(QNode));
if(!q->front)
exit(0);
q->front->next = NULL;
- 入队列(插入)操作
InsertQueue(LinkQueue *q ,ElemType e)
{
QueuePtr p;
p = (QueuePtr)malloc(sizeof(QNode));
if(p == NULL)
exit(0);
p->data = e;
p->next = NULL;
q->rear->next = p;
q->rear = p;
}
- 出队列(删除)操作
出队列操作时将队列中的第一个元素移除,队头指针不发生改变,改变头结点的next指针即可。
如果原队列只有一个元素,那么我们就应该处理一下队尾指针。
DeleteQueue(LinkQueue *q, ElemType *e)
{
QueuePtr p;
if(q->front == q->rear)
return;
p = q->front->next;
*e =p->data;
q->front ->next = p->next;
if(q->rear == p)
q->rear = q->front;
free(p);
- 销毁一个队列
由于链队列建立在内存的动态区,因此当一个队列不再有用时应当把它及时销毁掉,以免过多地占用内存空间。
DestroyQueue(LinkQueue *q)
{
while(q->front){
q->rear = q->front->next;
free(q->front);
q->front = q->rear;
}
}
队列的链式存储结构也可以用一个单链表实现。插入和删除操作分别在链表的两头进行;