队列文档之顺序队列

顺序队

定义

概念

队列的顺序实现指分配一块连续的存储单元存放队列中的元素,并附设两个指针:

  • 队头指针 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=3queue.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,这个什么情况?

顺序队列-百度百科

练习题

无。

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值