首页 > 其他分享 >05 线性结构——队列(特性、入队与出队、顺序队列和链式队列、顺序队列的“假溢出”问题及解决方案、相关功能的定义与代码实现)

05 线性结构——队列(特性、入队与出队、顺序队列和链式队列、顺序队列的“假溢出”问题及解决方案、相关功能的定义与代码实现)

时间:2024-10-16 22:53:32浏览次数:3  
标签:顺序 队列 元素 NULL queue 入队 size 指针

目录

1 队列的基本概念

1.1 定义

1.2 队列的组成部分

1.3 空队列

1.4 操作流程

 1.4.1 添加元素(入队)

1.4.2 删除元素(出队)

2 队列的存储结构

2.1 顺序队列

2.2 链式队列

3 顺序队列中的 “假溢出” 问题及解决方案

3.1 问题描述

3.2 解决方案

方法1:元素左移法

方法2:循环队列

4 功能定义

5 功能实现

5.1 初始化队列

5.2 返回队列内元素个数

5.3 添加新元素(入队)

5.4 循环队列的扩容问题

操作复杂性

具体步骤

5.5 删除元素(出队)并返回

5.6 释放队列内存

5.7 遍历队列

5.8 完整代码


1 队列的基本概念

1.1 定义

        队列(Queue)是一种特殊类型的线性表,其特点是只允许在一端进行插入(入队或进队)操作,在另一端进行删除(出队或离队)操作。这种数据结构遵循先进先出(First In First Out, FIFO)的原则,即最先加入队列的元素将最先被移除。

1.2 队列的组成部分

        队首(front): 队列中允许进行删除操作的一端。当元素从队列中移除时,总是从这一端开始。

        队尾(rear): 队列中允许进行插入操作的一端。新元素总是添加到这一端。

1.3 空队列

        当队列中没有任何元素时,我们称其为空队列。此时,队首和队尾的概念暂时不存在,直到有新的元素加入队列。

1.4 操作流程

        假设有一个空队列,当我们依次向其中添加元素 a1, a2, ..., an 后,a1 将成为队首元素,而 an 则成为队尾元素。因此,当需要从队列中移除元素时,按照 FIFO 原则,移除的顺序也必然是 a1, a2, ..., an。类似于生活中的排队行为。

 1.4.1 添加元素(入队)

        在添加元素时,通常的步骤如下:

  1. 检查队列是否已满:在添加元素之前,首先需要检查队列是否已满。如果队列已满,则不能继续添加元素。
  2. 存放元素:将新元素存放到队列的队尾位置。
  3. 移动队尾指针:将队尾指针 rear 向前移动一位,指向下一个空闲位置。

1.4.2 删除元素(出队)

        在删除元素时,通常的步骤如下:

  1. 检查队列是否为空:在删除元素之前,首先需要检查队列是否为空。如果队列为空,则不能继续删除元素。
  2. 获取队首元素:将队首元素保存在一个临时变量中,以便返回。
  3. 移动队首指针:将队首指针 front 向前移动一位,指向下一个元素。
  4. 返回队首元素:返回保存的队首元素。

2 队列的存储结构

        队列可以采用不同的方式来实现,主要分为以下两种:

2.1 顺序队列

        顺序队列使用数组作为底层存储结构。在这种情况下,队列的元素连续地存储在数组中。为了高效地管理队列,通常会维护两个指针,一个指向队首,另一个指向队尾。然而,顺序队列存在一个潜在的问题,即 “假溢出” 现象,即队列的实际存储空间未满,但由于队首和队尾指针的位置关系导致无法继续插入新的元素。

2.2 链式队列

        链式队列利用链表作为底层存储结构。每个节点包含数据部分和指向下一个节点的指针。与顺序队列相比,链式队列没有固定的容量限制,可以根据需要动态增加或减少内存。链式队列的优点在于它能够避免顺序队列中的 “假溢出” 问题,并且更容易实现队列的动态扩展。然而,链式队列的访问效率可能不如顺序队列,因为链表中的元素不是连续存储的。


3 顺序队列中的 “假溢出” 问题及解决方案

3.1 问题描述

        在顺序队列中,如果队列的尾指针 rear 已经到达数组的末尾,即使队列中有许多空闲位置(前面的元素删除后就会有位置),也无法再进行入队操作。这种情况被称为 “假溢出”。如下图所示:

3.2 解决方案

方法1:元素左移法

入队操作:

  • 将新元素添加到队列的最后一个位置。

出队操作:

  • 移除队首元素。
  • 将队列中剩余的所有元素向左移动一位,以填补空缺。

优点:

  • 实现简单,不需要额外的空间。

缺点:

  • 每次出队操作都需要移动所有后续元素,时间复杂度为 O(n),效率较低。

方法2:循环队列

概念:

        将队列的存储空间视为一个首尾相接的圆环,即当尾指针 rear 到达数组末尾时,可以将其重置为数组的起始位置,一般通过模运算进行操作


实现:

  • 使用两个指针 front 和 rear 分别指向队首和队尾。
  • 当 rear 达到数组末尾时,将其设置为数组的起始位置(queue->rear = (queue->rear + 1) % queue->capacity)。例如,假设队列的容量 capacity 为 5,rear 为 4(即数组的最后一个位置),那么 (4 + 1) % 5 的结果是 0,rear 会被更新为 0,即数组的起始位置。
  • 同样,当 front 达到数组末尾时,将其设置为数组的起始位置(queue->front = (queue->front + 1) % queue->capacity)。例如,假设队列的容量 capacity  为 5,front 为 4(即数组的最后一个位置),那么 (4 + 1) % 5 的结果是 0,front 会被更新为 0,即数组的起始位置。

优点:

  • 高效利用存储空间,避免了 “假溢出” 问题。
  • 入队和出队操作的时间复杂度均为 O(1)。

缺点:

        需要额外处理队列满和队列空的情况。


4 功能定义

        前文已提及,队列的底层实现既可以选择数组也可以选择链表。本节将基于动态数组来实现一个顺序队列,并定义以下必需的功能函数:

功能函数签名描述
初始化队列void initQueue(Queue *queue, size_t capacity)初始化一个队列,指定其容量。
返回队列内元素个数size_t getSize(const Queue *queue)返回队列中当前的元素个数。
添加新元素void enqueue(Queue *queue, int element)将一个新元素(假设是 int 类型)添加到队列的队尾。
元素出队列int dequeue(Queue *queue)从队列的队首移除一个元素并返回该元素(假设是 int 类型)。
释放队列内存void destroyQueue(Queue *queue)释放队列占用的内存。
遍历队列void printQueue(Queue *queue)打印队列中的所有元素。

5 功能实现

        本节将实现一个队列,基于动态数组(即顺序队列)以提供灵活性,并引入循环队列以解决假溢出问题。

5.1 初始化队列

#include <stdio.h>
#include <stdlib.h>

// 队列结构
typedef struct
{
    int *data;       // 动态数组存储队列元素
    size_t size;     // 队列内元素个数
    size_t capacity; // 动态数组的容量
    size_t front;    // 队列头指针
    size_t rear;     // 队列尾指针
} Queue;

/**
 * 初始化队列
 *
 * 此函数用于初始化一个队列,并分配初始容量的内存。
 *
 * @param queue 指向队列结构体的指针
 * @param initialCapacity 队列的初始容量
 *
 * @return 无返回值
 *
 * @note
 * - 如果传入的 `queue` 指针为 NULL,函数将输出错误信息并返回。
 * - 如果传入的 `initialCapacity` 为 0,函数将输出警告信息并返回。
 * - `size_t` 是 C 标准库中定义的一种无符号整数类型,不能表示负数。因此,传入的 `initialCapacity` 应该是一个正数。
 * - 成功初始化队列后,函数将输出一条成功消息。
 */
void initQueue(Queue *queue, size_t initialCapacity)
{
    // 检查传入的指针是否为空
    if (queue == NULL)
    {
        puts("Error: 传入的队列为 NULL");
        return; // 如果是空指针,则直接返回,退出函数
    }

    // size_t 是 C 标准库中定义的一种无符号整数类型,不能表示负数
    // 如果传入的 initialCapacity 是负数,可能会表示为一个很大的数(补码、数据回绕处理)
    // 函数内部不方便处理负数行为,只能在函数调用之前,约束传入的参数
    // 所以这里只是检查是否为 0
    if (initialCapacity == 0)
    {
        puts("Warning: 初始容量为 0,请使用一个正数来表示容量!");
        return; // 如果容量为 0,退出函数
    }

    // 分配初始容量的内存
    queue->data = (int *)malloc(initialCapacity * sizeof(int));
    if (queue->data == NULL)
    {
        puts("Error: 内存分配失败,队列初始化失败");
        return; // 如果内存分配失败,退出函数
    }

    queue->size = 0;                   // 初始元素个数为 0
    queue->capacity = initialCapacity; // 设置容量
    queue->front = 0;                  // 队列头指针
    queue->rear = 0;                   // 队列尾指针

    printf("Success: 成功初始化动态数组,容量为:%zu\n", initialCapacity);
}

int main()
{
    Queue myQueue;

    puts("----------------------1.初始化队列----------------------");
    initQueue(&myQueue, 2);
    initQueue(NULL, 2);     // 错误测试
    initQueue(&myQueue, 0); // 错误测试

    return 0;
}

        输出结果如下所示:

5.2 返回队列内元素个数

/**
 * 返回队列内元素个数
 *
 * 此函数用于获取队列中当前的元素个数。
 *
 * @param queue 指向队列结构体的指针
 *
 * @return 队列中的元素个数
 *
 * @note
 * - 如果传入的 `queue` 指针为 NULL,函数将输出错误信息并返回 0。
 * - 该函数不会修改队列的状态。
 */
size_t getSize(const Queue *queue)
{
    // 检查传入的指针是否为空
    if (queue == NULL)
    {
        puts("Error: 传入的队列为 NULL");
        return 0; // 如果是空指针,则直接返回 0,退出函数
    }

    return queue->size;
}

5.3 添加新元素(入队)

/**
 * 添加新元素
 *
 * 此函数用于将一个新元素添加到队列的尾部。
 *
 * @param queue 指向队列结构体的指针
 * @param element 要添加的新元素
 *
 * @return 无返回值
 *
 * @note
 * - 如果传入的 `queue` 指针为 NULL,函数将输出错误信息并返回。
 * - 如果队列已满,函数将输出错误信息并返回。
 * - 该函数会更新队列的尾指针和元素个数。
 */
void enqueue(Queue *queue, int element)
{
    // 检查传入的指针是否为空
    if (queue == NULL)
    {
        puts("Error: 传入的队列为 NULL");
        return;
    }

    // 检查队列是否已满
    if (queue->size == queue->capacity)
    {
        printf("队列已满,元素添加失败\n");
        // 暂时先不进行循环队列的扩容,因为循环队列的扩容有点麻烦
        return;
    }

    // 将新元素添加到队列尾部
    // 添加元素,只能在队尾进行
    queue->data[queue->rear] = element;

    // 循环更新队列尾指针
    // queue->rear++; // 这种简单的方式,会出现【假溢出】问题
    // 应该使用下面这种方式
    queue->rear = (queue->rear + 1) % queue->capacity;
    // 例如,假设 capacity 为 5,rear 为 4(即数组的最后一个位置)
    // 那么 (4 + 1) % 5 的结果是 0,rear 会被更新为 0,即数组的起始位置。

    // 更新元素个数
    queue->size++;

    printf("Success:元素(%d)已成功添加到队列\n", element);
}

5.4 循环队列的扩容问题

        循环队列的扩容比普通队列的扩容要复杂一些。主要原因在于循环队列的元素可能分布在数组的不同部分,而且扩容时需要保证元素的相对顺序不变。以下是详细的解释:

操作复杂性

  1. 元素分布不连续

    • 在循环队列中,元素可能分布在数组的不同部分。例如,假设队列的容量为 5,front 为 3,rear 为 1,size 为 4,数组的状态可能是 [4, _, 1, 2, 3]。在这种情况下,元素 1、2、3、4 分布在数组的不同部分。
  2. 保持元素顺序

    • 扩容时,需要将现有的元素按顺序复制到新的更大的数组中。由于元素在原数组中分布不连续,直接复制会导致顺序混乱。
  3. 重新计算指针

    • 扩容后,需要重新计算 front 和 rear 指针的位置,以确保它们在新的数组中仍然指向正确的元素。

具体步骤

  1. 创建新数组

    • 创建一个新的数组,容量通常是原数组的两倍或更大。
  2. 复制元素

    • 从 front 开始,逐个将元素复制到新数组中,直到复制完 size 个元素。这一步需要使用模运算来处理循环特性。
  3. 更新指针

    • 更新 front 和 rear 指针,使其在新数组中指向正确的元素。通常,新的 front 会设为 0,新的 rear 会设为 size
  4. 释放旧数组

    • 释放原来的数组,将新数组赋值给队列的 data 指针。

提示:

        由于循环队列的扩容涉及复杂的元素复制和指针更新,这里不进行单独的代码展示。

5.5 删除元素(出队)并返回

/**
 * 元素出队列
 *
 * 此函数用于从队列的头部移除一个元素。
 *
 * @param queue 指向队列结构体的指针
 *
 * @return 移除的元素,如果队列为空则返回 -1
 *
 * @note
 * - 如果传入的 `queue` 指针为 NULL,函数将输出错误信息并返回 -1。
 * - 如果队列为空,函数将返回 -1。
 * - 该函数会更新队列的头指针和元素个数。
 */
int dequeue(Queue *queue)
{
    if (queue == NULL)
    {
        puts("Error: 传入的队列为 NULL");
        return -1;
    }

    // 注意:不能通过判断 front 和 rear 是否相等来判定队列是否为空
    // front 和 rear 相等,可以是队列空了也可以是队列满了
    // 需要通过 queue->size 来判断
    if (queue->size == 0)
    {
        puts("Warning: 队列中已无元素可删除");
        return -1; // 队列为空,返回无效值
    }

    // 获取要出队的元素
    // 删除元素,只能在队首进行
    int dequeuedElement = queue->data[queue->front];

    // 循环更新队列头指针
    queue->front = (queue->front + 1) % queue->capacity;

    // 更新元素个数
    queue->size--;

    return dequeuedElement; // 返回出队的元素
}

5.6 释放队列内存

/**
 * 释放队列内存
 *
 * 此函数用于释放队列所占用的内存,并重置队列的各个属性。
 *
 * @param queue 指向队列结构体的指针
 *
 * @return 无返回值
 *
 * @note
 * - 如果传入的 `queue` 指针为 NULL,函数将输出错误信息并返回。
 * - 该函数会释放动态数组内存,并将队列的各个属性重置为初始状态。
 */
void destroyQueue(Queue *queue)
{
    if (queue == NULL)
    {
        puts("Error: 传入的队列为 NULL");
        return;
    }

    free(queue->data);  // 释放动态数组内存
    queue->data = NULL; // 避免悬挂指针
    queue->size = 0;
    queue->capacity = 0;
    queue->front = 0;
    queue->rear = 0;
    puts("Success:队列所占用的内存已成功释放^_^");
}

5.7 遍历队列

        注意,循环队列的设计使得队列的头指针 front 和尾指针 rear 可能在数组中不是连续的。具体来说,当 rear 达到数组末尾时,它会循环回到数组的起始位置。因此,队列中的元素可能分布在数组的不同部分。所以在遍历循环队列时,不能通过简单的循环来进行遍历,具体操作应如下所示:

/**
 * 遍历队列
 *
 * 此函数用于遍历队列中的所有元素并打印。
 *
 * @param queue 指向队列结构体的指针
 *
 * @return 无返回值
 *
 * @note
 * - 如果传入的 `queue` 指针为 NULL,函数将输出错误信息并返回。
 * - 该函数会从队列头指针开始,遍历队列中的所有元素并打印。
 */
void printQueue(Queue *queue)
{
    if (queue == NULL)
    {
        puts("Error: 传入的队列为 NULL");
        return; // 队列为空指针,退出函数
    }

    if (queue->size == 0)
    {
        puts("Warning: 队列为空队列,已无元素可遍历");
        return; // 队列为空,退出函数
    }

    puts("开始遍历队列元素:");

    // 不能使用下面这种行为进行遍历
    // 循环队列的设计使得队列的头指针 front 和尾指针 rear 可能在数组中不是连续的。
    // 具体来说,当 rear 达到数组末尾时,它会循环回到数组的起始位置。
    // 因此,队列中的元素可能分布在数组的不同部分。
    // for (int i = 0; i < queue->size; i++)
    // {
    //     printf("%d", queue->data[i]);
    // }

    // 正确的做法是从 front 开始遍历,直到遍历完 size 个元素
    for (int i = queue->front, j = 0; j < queue->size; i++, j++)
    {
        int data = queue->data[i % queue->capacity];
        // i % queue->capacity 确保 i 始终在数组的有效索引范围内。
        // 例如,假设 capacity 为 5,i 为 4,那么(4 + 1) % 5 的结果是 0,
        // 这样 i 会从 4 变为 0,即数组的起始位置。 printf("%d  ", data);

        printf("%d  ", data);
    }
    printf("\n");
}

5.8 完整代码

#include <stdio.h>
#include <stdlib.h>

// 队列结构
typedef struct
{
    int *data;       // 动态数组存储队列元素
    size_t size;     // 队列内元素个数
    size_t capacity; // 动态数组的容量
    size_t front;    // 队列头指针
    size_t rear;     // 队列尾指针
} Queue;

/**
 * 初始化队列
 *
 * 此函数用于初始化一个队列,并分配初始容量的内存。
 *
 * @param queue 指向队列结构体的指针
 * @param initialCapacity 队列的初始容量
 *
 * @return 无返回值
 *
 * @note
 * - 如果传入的 `queue` 指针为 NULL,函数将输出错误信息并返回。
 * - 如果传入的 `initialCapacity` 为 0,函数将输出警告信息并返回。
 * - `size_t` 是 C 标准库中定义的一种无符号整数类型,不能表示负数。因此,传入的 `initialCapacity` 应该是一个正数。
 * - 成功初始化队列后,函数将输出一条成功消息。
 */
void initQueue(Queue *queue, size_t initialCapacity)
{
    // 检查传入的指针是否为空
    if (queue == NULL)
    {
        puts("Error: 传入的队列为 NULL");
        return; // 如果是空指针,则直接返回,退出函数
    }

    // size_t 是 C 标准库中定义的一种无符号整数类型,不能表示负数
    // 如果传入的 initialCapacity 是负数,可能会表示为一个很大的数(补码、数据回绕处理)
    // 函数内部不方便处理负数行为,只能在函数调用之前,约束传入的参数
    // 所以这里只是检查是否为 0
    if (initialCapacity == 0)
    {
        puts("Warning: 初始容量为 0,请使用一个正数来表示容量!");
        return; // 如果容量为 0,退出函数
    }

    // 分配初始容量的内存
    queue->data = (int *)malloc(initialCapacity * sizeof(int));
    if (queue->data == NULL)
    {
        puts("Error: 内存分配失败,队列初始化失败");
        return; // 如果内存分配失败,退出函数
    }

    queue->size = 0;                   // 初始元素个数为 0
    queue->capacity = initialCapacity; // 设置容量
    queue->front = 0;                  // 队列头指针
    queue->rear = 0;                   // 队列尾指针

    printf("Success: 成功初始化动态数组,容量为:%zu\n", initialCapacity);
}

/**
 * 返回队列内元素个数
 *
 * 此函数用于获取队列中当前的元素个数。
 *
 * @param queue 指向队列结构体的指针
 *
 * @return 队列中的元素个数
 *
 * @note
 * - 如果传入的 `queue` 指针为 NULL,函数将输出错误信息并返回 0。
 * - 该函数不会修改队列的状态。
 */
size_t getSize(const Queue *queue)
{
    // 检查传入的指针是否为空
    if (queue == NULL)
    {
        puts("Error: 传入的队列为 NULL");
        return 0; // 如果是空指针,则直接返回 0,退出函数
    }

    return queue->size;
}

/**
 * 添加新元素
 *
 * 此函数用于将一个新元素添加到队列的尾部。
 *
 * @param queue 指向队列结构体的指针
 * @param element 要添加的新元素
 *
 * @return 无返回值
 *
 * @note
 * - 如果传入的 `queue` 指针为 NULL,函数将输出错误信息并返回。
 * - 如果队列已满,函数将输出错误信息并返回。
 * - 该函数会更新队列的尾指针和元素个数。
 */
void enqueue(Queue *queue, int element)
{
    // 检查传入的指针是否为空
    if (queue == NULL)
    {
        puts("Error: 传入的队列为 NULL");
        return;
    }

    // 检查队列是否已满
    if (queue->size == queue->capacity)
    {
        printf("队列已满,元素添加失败\n");
        // 暂时先不进行循环队列的扩容,因为循环队列的扩容有点麻烦
        return;
    }

    // 将新元素添加到队列尾部
    // 添加元素,只能在队尾进行
    queue->data[queue->rear] = element;

    // 循环更新队列尾指针
    // queue->rear++; // 这种简单的方式,会出现【假溢出】问题
    // 应该使用下面这种方式
    queue->rear = (queue->rear + 1) % queue->capacity;
    // 例如,假设 capacity 为 5,rear 为 4(即数组的最后一个位置)
    // 那么 (4 + 1) % 5 的结果是 0,rear 会被更新为 0,即数组的起始位置。

    // 更新元素个数
    queue->size++;

    printf("Success:元素(%d)已成功添加到队列\n", element);
}

/**
 * 元素出队列
 *
 * 此函数用于从队列的头部移除一个元素。
 *
 * @param queue 指向队列结构体的指针
 *
 * @return 移除的元素,如果队列为空则返回 -1
 *
 * @note
 * - 如果传入的 `queue` 指针为 NULL,函数将输出错误信息并返回 -1。
 * - 如果队列为空,函数将返回 -1。
 * - 该函数会更新队列的头指针和元素个数。
 */
int dequeue(Queue *queue)
{
    if (queue == NULL)
    {
        puts("Error: 传入的队列为 NULL");
        return -1;
    }

    // 注意:不能通过判断 front 和 rear 是否相等来判定队列是否为空
    // front 和 rear 相等,可以是队列空了也可以是队列满了
    // 需要通过 queue->size 来判断
    if (queue->size == 0)
    {
        puts("Warning: 队列中已无元素可删除");
        return -1; // 队列为空,返回无效值
    }

    // 获取要出队的元素
    // 删除元素,只能在队首进行
    int dequeuedElement = queue->data[queue->front];

    // 循环更新队列头指针
    queue->front = (queue->front + 1) % queue->capacity;

    // 更新元素个数
    queue->size--;

    return dequeuedElement; // 返回出队的元素
}

/**
 * 释放队列内存
 *
 * 此函数用于释放队列所占用的内存,并重置队列的各个属性。
 *
 * @param queue 指向队列结构体的指针
 *
 * @return 无返回值
 *
 * @note
 * - 如果传入的 `queue` 指针为 NULL,函数将输出错误信息并返回。
 * - 该函数会释放动态数组内存,并将队列的各个属性重置为初始状态。
 */
void destroyQueue(Queue *queue)
{
    if (queue == NULL)
    {
        puts("Error: 传入的队列为 NULL");
        return;
    }

    free(queue->data);  // 释放动态数组内存
    queue->data = NULL; // 避免悬挂指针
    queue->size = 0;
    queue->capacity = 0;
    queue->front = 0;
    queue->rear = 0;
    puts("Success:队列所占用的内存已成功释放^_^");
}

/**
 * 遍历队列
 *
 * 此函数用于遍历队列中的所有元素并打印。
 *
 * @param queue 指向队列结构体的指针
 *
 * @return 无返回值
 *
 * @note
 * - 如果传入的 `queue` 指针为 NULL,函数将输出错误信息并返回。
 * - 该函数会从队列头指针开始,遍历队列中的所有元素并打印。
 */
void printQueue(Queue *queue)
{
    if (queue == NULL)
    {
        puts("Error: 传入的队列为 NULL");
        return; // 队列为空指针,退出函数
    }

    if (queue->size == 0)
    {
        puts("Warning: 队列为空队列,已无元素可遍历");
        return; // 队列为空,退出函数
    }

    puts("开始遍历队列元素:");

    // 不能使用下面这种行为进行遍历
    // 循环队列的设计使得队列的头指针 front 和尾指针 rear 可能在数组中不是连续的。
    // 具体来说,当 rear 达到数组末尾时,它会循环回到数组的起始位置。
    // 因此,队列中的元素可能分布在数组的不同部分。
    // for (int i = 0; i < queue->size; i++)
    // {
    //     printf("%d", queue->data[i]);
    // }

    // 正确的做法是从 front 开始遍历,直到遍历完 size 个元素
    for (int i = queue->front, j = 0; j < queue->size; i++, j++)
    {
        int data = queue->data[i % queue->capacity];
        // i % queue->capacity 确保 i 始终在数组的有效索引范围内。
        // 例如,假设 capacity 为 5,i 为 4,那么(4 + 1) % 5 的结果是 0,
        // 这样 i 会从 4 变为 0,即数组的起始位置。 printf("%d  ", data);

        printf("%d  ", data);
    }
    printf("\n");
}

int main()
{
    Queue myQueue;

    puts("----------------------1.初始化队列----------------------");
    initQueue(&myQueue, 2);
    initQueue(NULL, 2);     // 错误测试
    initQueue(&myQueue, 0); // 错误测试

    puts("----------------------2.元素入队----------------------");
    enqueue(&myQueue, 1);
    enqueue(&myQueue, 2);
    printf("队列中元素的个数为:%d\n", getSize(&myQueue));
    enqueue(&myQueue, 3); // 满了,无法入队
    enqueue(NULL, 4);     // 错误测试
    printf("队列中元素的个数为:%d\n", getSize(&myQueue));

    puts("----------------------3.遍历队列----------------------");
    printQueue(&myQueue); // 遍历队列
    printQueue(NULL);     // 错误测试

    puts("----------------------4.元素出队----------------------");
    printf("出队的元素是:%d\n", dequeue(&myQueue));
    printf("出队的元素是:%d\n", dequeue(&myQueue));
    printf("队列中元素的个数为:%d\n", getSize(&myQueue));
    printQueue(&myQueue); // 遍历队列,没有元素可被遍历了

    puts("----------------------4.释放队列内存----------------------");
    destroyQueue(NULL); // 错误测试
    destroyQueue(&myQueue);

    return 0;
}

        输出结果如下所示:

标签:顺序,队列,元素,NULL,queue,入队,size,指针
From: https://blog.csdn.net/qq_53139964/article/details/142979422

相关文章

  • FreeRTOS:消息队列
    目录一、简介二、特点三、消息队列控制块四、相关API五、使用场景 一、简介        FreeRTOS的消息队列(MessageQueue)是任务之间通信的一种常用机制,允许任务或中断将数据发送到队列中,其他任务从队列中读取数据。        消息队列在嵌入式实时操作......
  • RabbitMQ系列学习笔记(三)--工作队列模式
    文章目录一、工作队列模式原理二、工作队列模式实战1、抽取工具类2、消费者代码3、生产者代码4、查看运行结果本文参考尚硅谷RabbitMQ教程丨快速掌握MQ消息中间件rabbitmqRabbitMQ详解Centos7环境安装Erlang、RabbitMQ详细过程(配图)一、工作队列模式原理与......
  • Linux多进程通信--管道、消息队列、共享内存
    转载至https://www.cnblogs.com/LUO77/p/5816326.html多进程:首先,先来讲一下fork之后,发生了什么事情。由fork创建的新进程被称为子进程(childprocess)。该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是新进程(子进程)的进程id。将子进程id返......
  • 数据结构--顺序表
    简介:这是顺序表的数据结构以C/C++语言实现,编译器为VS2022,如有不对的地方欢迎大家在评论区里讨论在其中我们要用到如下头文件:#include"stdio.h"#include"stdlib.h"简单宏定义一些类型,宏定义的内容可以根据自身需求进行更换:#defineMaxsize50 //静态顺序表的最大长度#def......
  • Gateway过滤器执行顺序以及跨域问题
    执行顺序请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执......
  • 顺序查找、二分查找
    一、基本查找(顺序查找):从前往后,挨个查找二、二分查找(折半查找)1、前提条件:数组中的数据必须是有序的。2、核心思想:每次排除一半的数据,查询数据的性能明显提高极多。举例:从一组数据中,找出79。第一步:从小到大排序。第二步:此时目标值79小于81,因此要将right移到mid左侧。......
  • 回文(栈和队列两种方法实现)
    includeincludeusingnamespacestd;typedefstruct{char*base;intfront;intrear;}SqQueqe;typedefstruct{char*base;char*top;intstacksize;}SqStack;//初始化栈voidinitStack(SqStack&s){s.base=newchar[101];s.top=s.base;s.stacksize=......
  • 代码随想录算法训练营第三十四天|134. 加油站 135. 分发糖果 860.柠檬水找零 406.根据
    134.加油站在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。你有一辆油箱容量无限的的汽车,从第i个加油站开往第i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。如果你可以绕环路行驶一周,则返回出发时加油站的......
  • 消息队列之RabbitMQ
    1.初识MQ在分布式微服务中,不同服务接口之间的调用分为同步调用和异步调用。使用同步调用有几种问题拓展性差性能差级联失败因此在大部分场景,我们使用的都是异步调用。异步调用方式其实就是基于消息通知的方式,一般包含三个角色:消息发送者:投递消息的人,就是调用方消息Bro......
  • 微服务02 Kafka消息队列, Dubbo, Springcloud微服务框架, Nacos
    3.6Kafka部署kafka下载链接http://kafka.apache.org/downloads#清华源https://mirrors.tuna.tsinghua.edu.cn/apache/kafka/kafka版本格式kafka_<scala版本>_<kafka版本>#示例:kafka_2.13-2.7.0.tgz官方文档:http://kafka.apache.org/quickstart#二进制安装......