数据结构(10)队列之顺序队列
前言
在队列的顺序存储结构中,除了用一组地址连续的存储单元来存放元素之外,一般还会附设两个指针fount和rear来指示队头和队尾元素的位置,这样更有队列的特点,但是它本质上仍是顺序表。
顺序队列的初始化与插入删除
通常约定,初始化空队列时,让fount和rear初始化为0,每当需要插入元素(即新元素入队)时,尾指针(rear)加一;要删除元素(即队首出队)时,头指针(fount)加一。这样,在非空顺序队列里,头指针始终指向队头元素,尾指针则指向队尾元素的下一个位置。
遍历时,从fount指示的队首位置开始,到rear指示的前一个位置结束。
顺序队列所存在的问题
在顺序队列中,当我们要执行插入操作时,首先需要判断队列是否已满,如果队列已满是无法插入元素的;而判断是否已满的条件是看rear指针是否大于等于队列最大容量。当然,在以往的顺序表中,也可以重新去申请内存来实现插入的需求。但是先看看到我们的删除操作,只是将fount指针自增,不进行其他操作。这样就会造成一个现象:队列逻辑上已经满了(rear的指向已经越界了),实际上仍是有空间的(队首元素之前的空间还可用)。
我们称这种现象为“假溢出”。显然,在假溢出的情况下去重新申请内存是不合适的,合理的考虑是先充分利用之前的空间,这样就出现了一种比较特殊的结构——循环队列,会在下篇博客中详细说明。
也有人会觉得:假如让fount指针固定为0,要出队时将队首元素后的所有元素向前移不就行了吗?这样的确可以解决假溢出的问题,但是每次出队都要移动大量元素,性能就降低了。这也是顺序队列大多都设置为循环队列的原因——既解决问题,又保证了性能。
全部代码
SeqQueue.h
#ifndef SeqQueue_h
#define SeqQueue_h
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define ElemType int
#define MAXSIZE 8
typedef struct Queue{
ElemType *base;
int fount;
int rear;
}Queue;
//初始化
void InitQueue(Queue *Q);
//入队
void EnQueue(Queue *Q,ElemType x