顺序队
定义
概念
队列的顺序实现指分配一块连续的存储单元存放队列中的元素,并附设两个指针:
- 队头指针 front 指向队头元素。
- 队尾指针 rear 指向队尾元素的下一个位置。
通常采用数组实现。
关于队列状态和操作如下:
- 初始状态:将队头指针 front 和队尾指针 rear 都指向 0。
- 队空状态:
queue.front==queue.rear
。
- 队满状态:
queue.rear==MAXSIZE
。
- 入队操作:队未满时,先赋值到队尾元素,再将队尾指针加一。
- 出队操作:队非空时,先取出队头元素值,再将队头指针加一。
注:
- 对于上面的队空和队满状态的判断实际上是存储错误的,甚至不能作为判断的条件。但是在这里让它条件成立,这里讨论的仅是普通的顺序队列,有缺陷的顺序队列。
- 一些参考书中是将
queue.front==queue.rear==0
作为队空的条件,这是正确的,而我上面仅仅是queue.front==queue.rear
作为队空的条件,这是有缺陷的,如『队空状态』图中下方的图,图中queue.front==queue.rear==3
,如果对这个队列进行入队操作,只能入队两次(即queue.rear=3
和queue.rear=4
的位置),但实际上下标为 0、1、2 的空间也是空的,也能入队,这就造成了空间浪费,所以书上才说不能作为队空的判定条件。而queue.front==queue.rear==0
把队头指针和队尾指针都固定在了第一个数组位置,即队列中所有位置都可以入队,不存在空间浪费,才是真正的队空。- 说完了队空,再来说说队满,书中说
queue.rear==MAXSIZE
不能作为队满的条件,如『队空状态』图中下方的图,队列中仅有两个元素,但却满足该条件,而另外三个空间却是空的(虽然有值,但不是有效的队列元素值,因为顺序队列删除元素后并不会修改原有空间的值,而只是移动指针)。这时候如果再入队,就会出现“上溢出”,但这种溢出并不是真正的溢出,在 data 数组中仍然存在可以存放元素的空位置,所以这是一种“假溢出”。- 队列空间未能充分利用,这就是顺序队列的缺点,但在本节中我们允许这种缺点的存在,在下一节中才讲解决这种缺点的循环队列。
- 通常我们所说的顺序队列,实际上是循环队列。但本节说的是这种带有缺陷的顺序队列,而非循环队列。
结构体
/**
* 顺序队列结构体定义
*/
typedef struct {
/**
* 数据域,存储队列中的数据
*/
int data[MAXSIZE];
/**
* 指针域,队头指针。在空队列中,队头指针指向 0;在非空队列中,队头指针始终指向队头元素
*/
int front;
/**
* 指针域,队尾指针。在空队列中,队尾指针指向 0;在非空队列中,队尾指针始终指向队尾元素的下一位置
*/
int rear;
} SeqQueue;
特点
front
表示队头指针,实际上就是数组下标,指向顺序队列的队头元素;rear
表示队尾指针,指向顺序队列的队尾元素的下一个位置。- 当队满(即
rear==MAXSIZE
)后再入队,就会发生上溢。即使队列中还有空闲空间。 - 入队操作,如果队不满才能入队,先赋值到队尾元素,再将队尾指针加一。
- 出队操作,如果队不空才能出队,先取出队头指针所指向的元素,再将队头指针加一。
基本操作
注,完整代码请参考:
概述
顺序队列的常见操作如下:
void init(SeqQueue *queue)
:初始化顺序队列。其中queue
表示顺序队列。int isEmpty(SeqQueue queue)
:判断顺序队列是否为空。其中queue
表示顺序队列。如果顺序队列为空则返回 1,否则返回 0。int isFull(SeqQueue queue)
:判断顺序队列是否为满。其中queue
表示顺序队列。如果顺序队列为满则返回 1,否则返回 0。int enQueue(SeqQueue *queue, int ele)
:将元素进队。其中queue
表示顺序队列;ele
表示待插入的新元素值。如果顺序队列为满则不能入队,则返回 0;如果入队成功则返回 1 表示成功。int deQueue(SeqQueue *queue, int *ele)
:将元素出队。其中queue
表示顺序队列。ele
用来存放出队的元素。如果顺序队列为空则不能出队,则返回 0;如果出队成功则返回 1 表示成功。int size(SeqQueue queue)
:获取顺序队列的长度。其中queue
表示顺序队列。返回顺序队列中元素个数。int getFront(SeqQueue queue, int *ele)
:获取顺序队列队头元素。其中queue
表示顺序队列;ele
用来存放队头元素。如果顺序队列为空则无法获取队头元素则返回 0,否则返回 1。int getRear(SeqQueue queue, int *ele)
:获取顺序队列队尾元素。其中queue
表示顺序队列;ele
用来存放队尾元素。如果顺序队列为空则无法获取队尾元素则返回 0,否则返回 1。void clear(SeqQueue *queue)
:清空顺序队列所有元素。其中queue
表示顺序队列。void print(SeqQueue queue)
:打印顺序队列中所有元素。其中queue
表示顺序队列。
init
初始化顺序队列。
实现步骤:
- 将队头指针
front
和队尾指针rear
都指向 0,表示空队列。
实现代码如下:
/**
* 初始化顺序队列
* @param queue 未初始化的顺序队列
*/
void init(SeqQueue *queue) {
// 初始化顺序队列,将队头指针和队尾指针都指向 0
// 有些实现中将队头指针和队尾指针指向 -1,这无所谓,只要能够实现队列功能即可
queue->front = 0;
queue->rear = 0;
}
isEmpty
判定顺序队列是否为空。如果为空则返回 1,否则返回 0 表示非空。
实现步骤:
- 判断队头指针
front
和队尾指针rear
是否指向同一位置,即queue.front==queue.rear
。
实现代码如下:
/**
* 判断顺序队列是否为空
* @param queue 顺序队列
* @return 如果顺序队列为空则返回 1,否则返回 0 表示非空队列
*/
int isEmpty(SeqQueue queue) {
// 如果队头指针和队尾指针都指向同一位置,则表示队列处于空
// 因为队头指针和队尾指针在不断变化,只要它们相等就表示是空队列,其中 front 和 rear 可能是 0,有可能是 4,而非一直都初始化值
if (queue.front == queue.rear) {
return 1;
} else {
return 0;
}
}
isFull
判断顺序队列是否已经满队,如果已满则返回 1,否则返回 0 表示未满。
实现步骤:
- 如果队尾指针
queue.rear==MAXSIZE
那么就认定它队满。
实现代码如下:
/**
* 判断顺序队列是否满
* @param queue 顺序队列
* @return 如果队列满则
*/
int isFull(SeqQueue queue) {
// 如果队尾指针指向了 MAXSIZE,即数组的最后一个位置,则表示队满
// 因为规定在非空队列中,队尾指针始终指向队尾元素的下一位置,所以当元素是队列数组最后一个元素(MAXSIZE-1)时,那么队尾指针一定指向 MAXSIZE
if (queue.rear == MAXSIZE) {
return 1;
} else {
return 0;
}
}
enQueue
将元素入队。如果队未满才能入队,否则返回 0 表示入队失败。如果入队成功则返回 1。以 queue=[11, 22, 33]; ele=44
为例如图:
实现步骤:
- 参数校验,如果队满则不能入队。返回 0 表示入队失败。
- 将元素入队。首先将队尾指针所指向的位置赋予元素值(因为队尾指针指向队尾元素的下一个位置,所以是空的能够直接赋予值);接着将队尾指针加一。
- 返回 1 表示入队成功。
实现代码如下:
/**
* 将元素入队,即插入到队尾
* @param queue 顺序队列
* @param ele 待入队的元素
* @return 如果入队成功则返回 1,否则返回 0 表示入队失败
*/
int enQueue(SeqQueue *queue, int ele) {
// 0.参数校验,如果队满,则不能入队
if (queue->rear == MAXSIZE) {
return 0;
}
// 1.将元素入队
// 1.1 先进行赋值,初始时队头指针指向 0
queue->data[queue->rear] = ele;
// 1.2 然后将队尾指针加一
queue->rear++;
// 1.3 返回 1 表示入队成功
return 1;
}
deQueue
将元素出队。如果队空则不能出队,返回 0 表示出队失败。将出队元素保存到 ele,并返回 1 表示出队成功。以 queue=[11, 22, 33]
为例如图所示:
实现步骤:
- 参数校验,如果队空,则不能出队。返回 0 表示出队失败。
- 将元素出队。用 ele 保存队头指针所指向的元素;然后将队头指针加一,保证队头指针始终指向队头元素。
- 返回 1 表示出队成功。
实现代码如下:
/**
* 将元素出队,即出队队头元素
* @param queue 顺序队列
* @param ele 用来保存出队元素
* @return 如果出队成功则返回 1,否则返回 0 表示出队失败
*/
int deQueue(SeqQueue *queue, int *ele) {
// 0.参数校验,如果队空则不能出队
if (queue->front == queue->rear) {
return 0;
}
// 1.将元素出队
// 1.1 用 ele 保存队头元素
*ele = queue->data[queue->front];
// 1.2 将队头指针加一,表示删除队头元素,使得队头指针始终指向队头元素
queue->front++;
// 1.3 返回 1 表示出队成功
return 1;
}
size
获取顺序队列中实际的元素个数。
实现步骤如下:
- 用队尾指针
rear
减去队头指针front
,即为元素个数。虽然队头指针和队尾指针都是数组下标,但队尾指针是指向队尾元素的下一个位置,所以相减的结果就是队列的实际元素个数。
实现代码如下:
/**
* 顺序队列中的有效元素个数
* @param queue 顺序队列
* @return 顺序队列元素个数
*/
int size(SeqQueue queue) {
// 由于队头指针始终指向队头元素,队尾指针指向队尾元素的下一个位置,所以元素个数是之差
return queue.rear - queue.front;
}
getFront
读取队头元素,但并不出队。如果队空则不能读取,则返回 0,否则用 ele 保存队头元素,返回 1 表示读取成功。以 queue=[22, 33]
为例如图:
实现步骤:
-
参数校验,如果队空则没有队头元素,自然也无法获取。返回 0 表示读取失败。
-
直接读取队头指针所指向的元素。因为队头指针始终指向队头元素。
实现代码如下:
/**
* 获取顺序队列的队头元素
* @param queue 顺序队列
* @param ele 用来保存队头元素
* @return 如果获取队头元素成功则返回 1,否则返回 0 表示获取失败
*/
int getFront(SeqQueue queue, int *ele) {
// 0.参数校验,如果是空队列,则不能获取到队头元素
if (queue.front == queue.rear) {
return 0;
}
// 1.用 ele 保存队头元素,即队头指针所指向的元素
*ele = queue.data[queue.front];
return 1;
}
getRear
读取顺序队列的队尾元素。如果顺序队空为空则返回 0 表示读取失败。否则用 ele 保存队尾元素,并返回 1 出队成功。以 queue=[11, 22, 33]
为例如图所示:
实现步骤:
- 参数校验,如果队空,则不能读取队尾元素。返回 0 表示读取失败。
- 读取队尾指针所指向位置的前一个位置的元素,用 ele 保存。因为队尾指针始终指向队尾元素的下一个位置,所以要读取队尾元素,需要读取到队尾指针的前一个位置的元素。
- 返回 1 表示读取成功。
实现代码如下:
/**
* 获取顺序队列的队尾元素
* @param queue 顺序队列
* @param ele 用来保存队尾元素
* @return 如果获取队尾元素成功则返回 1,否则返回 0 表示获取失败
*/
int getRear(SeqQueue queue, int *ele) {
// 0.参数校验,如果是空队列,则不能获取到队尾元素
if (queue.front == queue.rear) {
return 0;
}
// 1.获取队尾元素,由于队尾指针指向队尾元素的下一个位置,所以需要将队尾指针减一来获取队尾元素
*ele = queue.data[queue.rear - 1];
return 1;
}
clear
清空顺序队列。以 queue=[11, 22, 33]
为例如图:
实现步骤:
- 将顺序队列的队头指针
front
和队尾指针rear
都指向 0,表示空队列。但实际上队列中原有的元素仍然存在,并没有被重置为某个值。
实现代码如下:
/**
* 清空顺序队列
* @param queue 顺序队列
*/
void clear(SeqQueue *queue) {
// 清空顺序队列,将队头指针和队尾指针都置为 0
queue->front = 0;
queue->rear = 0;
}
print
打印队列中的所有有效元素。
实现步骤:
- 从队头指针开始扫描整个顺序队列,直到队尾指针结束,但不包括队尾指针所指向的元素。
实现代码如下:
/**
* 打印顺序队列中从队头到队尾的所有元素
* @param queue 顺序队列
*/
void print(SeqQueue queue) {
printf("[");
for (int i = queue.front; i < queue.rear; i++) {
printf("%d", queue.data[i]);
if (i != queue.rear - 1) {
printf(", ");
}
}
printf("]\n");
}
注意事项
一道数据结构题,请问,顺序队列中,初始时front=-1,rear=0,这个什么情况?
练习题
无。