循环队列详解

1. 循环队列

1.1 概念及结构

循环队列是一种特殊类型的队列数据结构,也被称为”唤醒缓冲器“。它在数组的基础上实现了循环利用空间的功能。在循环队列中,队尾和队头之间形成了一个循环,当队尾指针“追上”队头指针时,队列不再继续增长,而是继续利用之前出队的空间。

循环队列通常由两个指针来辅助构建:

  1. 队尾指针(rear):指向队尾元素的下一个位置,也就是即将插入新元素的位置。
  2. 队头指针(front):指向队头元素的位置

入队和出队:

  • 入队操作会将元素插入到队尾指针所指向的位置,并将队尾指针后移。当队列满时,入队操作会失败。

  • 出队操作会**删除队头元素,并将队头指针后移。**当队列为空时,出队操作会失败。

队空和队满的条件:

  • 当队列为空时,front指针和rear指针同时指向下标为0的位置,因此循环队列为空的条件为front == rear
  • 需要清楚,由于循环队列需要预留一个空间来区分队列为空和队列满的状态队列满的条件为rear + 1 == head

为什么需要预留一个空间?

假设我们不预留空间,要对下面的队列插入元素‘7’

那么插入后,front(head)rear(tail)两个指针的关系就变成了这样:

可以看到,frontrear又相等了,这不就和队列为空的条件混到一起了吗。所以说要多预留一个空间用来判断队列满的情况:

1.2 结构的定义

这里我们用数组来模拟实现循环队列

typedef struct {
    int *data;	//动态数组
    int front;	//队头指针
    int rear;	//队尾指针
    int capacity;	//最大容量
} MyCircularQueue

1.3 基本功能实现

1.3.1 初始化(返回一个循环队列指针)

MyCircularQueue* myCircularQueueCreate(int k);
  • k为循环队列的最大容量
  • 该函数用来返回一个已经初始化好的循环队列指针
MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* CQueue = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    
    CQueue->data = (int*)malloc(sizeof(int) * (k + 1));	//申请k+1个空间
    CQueue->front = 0;
    CQueue->rear = 0;
    CQueue->capacity = k;

    return CQueue;
}

1.3.2 判空

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->front == obj->rear;
}

1.3.3 判满

可不要简单的以为循环队列满的条件就是rear + 1 == front,我们要考虑下面两种情况(假设最大容量为4):

情况一:

情况二:

上面两种情况队列都是满的,显然我们不能简单的用front == rear + 1来判断队列是否已满。

直接下结论:我们可以用表达式(rear + 1) % (capacity + 1) == front来判断队列是否已满

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->rear + 1)  % (obj->capacit + 1) == obj->front;
}

1.3.4 入队

入队移动的是队尾指着,同样也要考虑两种情况:

情况一:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vIYd7ryM-1691591491729)(C:/Users/HUASHUO/AppData/Roaming/Typora/typora-user-images/image-20230809090716043.png)]

情况二:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A4WZgZn3-1691591491730)(C:/Users/HUASHUO/AppData/Roaming/Typora/typora-user-images/image-20230808114647693.png)]

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if (myCircularQueueIsFull(obj))	//如果已满,插入失败
        return false;

    //如果队尾指针在队列尾部,那么插入过后,队尾指针移到最前面
    if (obj->rear == obj->capacity)
    {
        obj->data[obj->rear] = value;
        obj->rear = 0;
    }
    //否则队尾指针加以即可
    else
    {
        obj->data[obj->rear] = value;
        obj->rear++;
    }

    return true;
}

1.3.5 出队

出队移动的是队头指针,考虑两种情况:

情况一:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i819wqgc-1691591491730)(C:/Users/HUASHUO/AppData/Roaming/Typora/typora-user-images/image-20230809091122868.png)]

情况二:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NmLHjGvL-1691591491731)(C:/Users/HUASHUO/AppData/Roaming/Typora/typora-user-images/image-20230809091358771.png)]

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))	//如果队列为空,则出队失败
        return false;

    //如果队头指针位于队列最后一个位置,那么删除后就要回到最前面
    if (obj->front == obj->capacity)
        obj->front = 0;
    //否则队头指针往后移即可
    else    
        obj->front++;

    return true;
}

1.3.5 返回队头元素

由于队头指针指向的就是队头元素,因此直接返回下标位置的元素即可

int myCircularQueueFront(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))	//队列为空,返回-1
        return -1;

    return obj->data[obj->front];
}

1.3.6 返回队尾元素

由于队尾指针指向的是队尾元素的下一个位置,因此要考虑两种情况:

情况一:

情况二:

int myCircularQueueRear(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))	//队列为空,返回-1
        return -1;

    //如果队尾指针在下标为0的位置,就说明队尾元素位于数组最后的位置
    if (obj->rear == 0)
        return obj->data[obj->capacity];
    //否则,队尾元素就是队尾指针下标-1的位置
    else
        return obj->data[obj->rear - 1];
}

1.3.7 销毁队列

void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->data);	//销毁数组
    free(obj);	//销毁队列
}

1.4 练习

学习完循环队列的相关知识,可以做这一题来加深印象👉设计循环队列

  • 9
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
C语言中,FIFO循环队列的实现可以使用数组或链表来实现。循环队列是一种先进先出(FIFO)的数据结构,它允许在队列的后端(称为rear)进行插入操作,在前端(称为front)进行删除操作。循环队列的实现需要记录队列的长度、队首和队尾的位置,以及队内有效元素的个数。 以下是使用数组实现FIFO循环队列的示例代码: ```c typedef struct { int* arr; // 存放数据的数组 int front; // 队首位置 int rear; // 队尾位置 int count; // 队内有效元素个数 int N; // 循环队列的长度 } MyCircularQueue; // 创建循环队列 MyCircularQueue* myCircularQueueCreate(int k) { MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue)); obj->arr = (int*)malloc(sizeof(int) * (k + 1)); // 开辟比有效数据多1个的空间 obj->N = k + 1; obj->front = obj->rear = obj->count = 0; return obj; } // 入队操作 bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) { if (obj->count == obj->N - 1) { return false; // 队列已满 } obj->arr[obj->rear = value; obj->rear = (obj->rear + 1) % obj->N; obj->count++; return true; } // 出队操作 bool myCircularQueueDeQueue(MyCircularQueue* obj) { if (obj->count == 0) { return false; // 队列为空 } obj->front = (obj->front + 1) % obj->N; obj->count--; return true; } // 获取队首元素 int myCircularQueueFront(MyCircularQueue* obj) { if (obj->count == 0) { return -1; // 队列为空 } return obj->arr[obj->front]; } // 获取队尾元素 int myCircularQueueRear(MyCircularQueue* obj) { if (obj->count == 0) { return -1; // 队列为空 } return obj->arr[(obj->rear - 1 + obj->N) % obj->N]; } // 检查队列是否为空 bool myCircularQueueIsEmpty(MyCircularQueue* obj) { return obj->count == 0; } // 检查队列是否已满 bool myCircularQueueIsFull(MyCircularQueue* obj) { return obj->count == obj->N - 1; } // 释放队列占用的内存空间 void myCircularQueueFree(MyCircularQueue* obj) { free(obj->arr); free(obj); } ``` 以上代码中,我们使用了一个数组(`arr`)来存放数据,通过维护 `front` 和 `rear` 来指示队首和队尾的位置,使用 `count` 来记录队内有效元素的个数,使用 `N` 来记录循环队列的长度。入队操作使用取模运算来实现循环队列的特性,出队操作和获取队首、队尾元素的操作也相应地进行了调整。通过以上的代码实现,我们可以实现一个C语言的FIFO循环队列。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C语言循环队列的表示与实现实例详解](https://download.csdn.net/download/weixin_38516040/14001535)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【数据结构循环队列(C语言实现)](https://blog.csdn.net/Hello_World_213/article/details/124822852)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [C语言实现循环队列](https://blog.csdn.net/weixin_62029250/article/details/121688210)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Forward♞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值