【数据结构】:队列补充内容--循环队列

循环队列

循环队列是一种使用有限数组来模拟队列的数据结构,它在逻辑上表现为先进先出(FIFO)的线性表,但在物理上则是利用数组来存储数据。循环队列的一个主要特点是它的头尾相连,形成一个环。当队列的尾部指针到达数组的最后一个元素时,它会自动跳回到数组的第一个元素开始继续存储数据,从而有效地利用了数组的存储空间。

循环队列的基本结构

循环队列通常需要两个指针来维护队列的状态:

  1. 队头指针(front):指向队列的第一个有效数据的位置。
  2. 队尾指针(rear):指向队列中最后一个有效数据的下一个位置,即下一个入队元素应该存放的位置。

在这里插入图片描述

循环队列的入队和出队操作

入队操作
  1. 对于循环队列,我们发现判断队满和队空的条件,均为rear==front,为了避免这种情况,通常会空出一个空间来检查队列是否已满。循环队列的满条件与普通的线性队列不同,它不能简单地判断rear == 数组长度,因为这样会浪费一个数组的存储空间。通常,循环队列的满条件是(rear + 1) % 数组长度 == front
  2. 如果队列未满,将新元素存储在rear所指向的位置,并将rear指针向前移动一位,即rear = (rear + 1) % 数组长度
出队操作
  1. 检查队列是否为空。循环队列的空条件是front == rear
  2. 如果队列不为空,取出front所指向的元素,并将front指针向前移动一位,即front = (front + 1) % 数组长度

循环队列的优缺点

优点
  • 节省空间:通过头尾相连的方式,有效地利用了数组的存储空间,避免了在队列的末尾还有大量空闲空间时,由于队列头部的空间已被占用,而无法入队新元素的问题。
  • 操作高效:入队和出队操作的时间复杂度均为O(1),即在常数时间内完成。
缺点
  • 空间浪费:循环队列在队列不满时,也会留下一个存储空间不被使用,以避免队空和队满的混淆。

循环队列代码实现

以力扣上的设计循环队列这道题为例

622.设计循环队列-力扣(LeetCode)
题目:

设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应该支持如下操作:

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。
  • Front: 从队首获取元素。如果队列为空,返回 -1 。
  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
  • isEmpty(): 检查循环队列是否为空。
  • isFull(): 检查循环队列是否已满。

循环队列存储结构描述为:

typedef struct {
    int *a;
    int front;//队头指针
    int rear;//队尾指针
    int k;//数组中存放的个数
} MyCircularQueue;

MyCircularQueue(k): 构造器,设置队列长度为 k 。

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if(obj==NULL){
        perror("malloc false");
        return NULL;
    }
    //给数组开辟k+1个空间,空一个空间更好判断
    //k+1为数组长度,但存放数据元素的个数是k,空一个空间更好判断队空和队满
    obj->a=(int*)malloc(sizeof(int)*(k+1));
    if(obj->a==NULL){
        perror("malloc false");
        return NULL;
    }
    obj->front=obj->rear=0;
    obj->k=k+1;
    return obj;
}

isEmpty(): 检查循环队列是否为空。

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    //队头指针等于队尾指针时就是空队列
    return obj->front==obj->rear;
}

isFull(): 检查循环队列是否已满。

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    //队尾指针加一取模等于队头指针时就是满队列
    return obj->front==(obj->rear+1)%(obj->k);
}

enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    //队列满直接返回false
    if(myCircularQueueIsFull(obj)){
        return false;
    }
    //不满队尾指针先存值再加一
    obj->a[obj->rear++]=value;
    //然后队尾指针取模
    obj->rear%=(obj->k);
    return true;
}

deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    //队列空直接返回false
    if(myCircularQueueIsEmpty(obj)){
        return false;
    }
    //不空队头指针加一
    obj->front++;
    //然后队头指针取模
    obj->front%=(obj->k);
    return true;
}

Front: 从队首获取元素。如果队列为空,返回 -1 。

int myCircularQueueFront(MyCircularQueue* obj) {
    //队空直接返回-1
    if(myCircularQueueIsEmpty(obj)){
        return -1;
    }
    else{
        return obj->a[obj->front];
    }
}

Rear: 获取队尾元素。如果队列为空,返回 -1 。

int myCircularQueueRear(MyCircularQueue* obj) {
    //队空直接返回-1
    if(myCircularQueueIsEmpty(obj)){
        return -1;
    }
    else{
        return obj->a[(obj->rear-1+obj->k)%(obj->k)];
    }
}

Free:销毁

void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->a);
    obj->a=NULL;
    free(obj);
    obj=NULL;
}

以上就是关于循环队列的讲解,如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!
在这里插入图片描述

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值