【数据结构】队列扩展--循环队列

循环队列的概念

循环队列是队列的顺序表示和实现,采用循环链表是为了解决顺序队列中的溢出现象。和顺序栈相类似,在队列的顺序存储结构中,除了用一组地址连续的存储单元依次存放从队头到队尾的元素之外,还需要设两个变量front,rear分别表示队头元素和队尾元素的位置。
当循环队列为空时,front =rear =0,每当插入一个数据时,尾指针加1;每当删除队头数据时,头指针加1。所以,在非空队列中,头指针front始终指向队头元素,尾指针rear始终指向队尾元素的下一个位置。
假设队列分配的最大空间为6,但是我们只用5个空间存储数据。
(1)空队列
在这里插入图片描述
(2)插入一个数据,rear+1,rear将指向队尾元素的下一个
在这里插入图片描述
(3)插入足够数据,使队列为满,rear指向下标为5的位置
在这里插入图片描述
(4)删除数据,front+1,front始终指向队头元素
在这里插入图片描述
(5)再次插入1个数据,此时rear已经指向数组的最后位置,由于我们使用的是循环队列,rear应该移动至下标为0处。【rear=(rear+1)%6】
在这里插入图片描述
(6)再次插入一个数据,队列再次为满
在这里插入图片描述
通过(3)(6)可以看出,循环队列为满时满足rear的下一位置为front,即(rear+1)%(k+1)== front

代码实现

1.结构体的定义

typedef struct {
    int* data; //用于队列存放数据
    int front; //队头指针
    int rear;//队尾指针
    int k;//队列空间大小
} MyCircularQueue;

2.函数功能实现

(1)MyCircularQueue* myCircularQueueCreate(int k); //初始化
(2)bool myCircularQueueIsEmpty(MyCircularQueue* obj);//判断是否为空
(3)bool myCircularQueueIsFull(MyCircularQueue* obj)//判断是否为满
(4)bool myCircularQueueEnQueue(MyCircularQueue* obj, int value);//入队
(5)bool myCircularQueueDeQueue(MyCircularQueue* obj);//出队
(6)int myCircularQueueFront(MyCircularQueue* obj);//获取队头元素
(7)int myCircularQueueRear(MyCircularQueue* obj);//获取队尾元素
(8)void myCircularQueueFree(MyCircularQueue* obj);//销毁

2.1初始化循环队列并返回队列指针

其中k是循环队列容量

MyCircularQueue* myCircularQueueCreate(int k) {
    int i = 0;
    MyCircularQueue* obj;
    obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    
    obj->front = 0;
    obj->rear = 0;
    obj->k = k;
    obj->data = (int*)malloc(sizeof(int) * (obj->k + 1));//队列长为k,开辟k+1个空间

    return obj;
}

2.2判断队列是否为空

当队列为空时,front == rear

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

2.3判断队列是否为满

根据之前的分析,队列为满要考虑两种情况,条件是**(rear+1)%(k)=front**

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

2.4入队

入队时首先要判断循环队列是否为满,满的话不能再执行入队操作了。若不为满,由于rear指向队尾的下一位置,所以先在rear处插入数据,再移动rear。

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if (myCircularQueueIsFull(obj))
    {
        return false;
    }
    else
    {
        obj->data[obj->rear] = value;
        obj->rear++;
        obj->rear %= (obj->k + 1);
        return true;
    }
}

2.5出队

执行出队操作时首先要判断循环队列是否为空,空的话不能再执行该操作了。若不为空,不用删除队头数据,直接移动front即可,因为数组的有效范围是front到rear间的数据。

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    else
    {
        obj->front++;
        obj->front %= (obj->k + 1);
        return true;
    }
}

2.6获取队头元素

front所指位置就是队头元素,所以直接返回数组中下标为front的值就可以了,前提是循环队列不为空。

int myCircularQueueFront(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->data[obj->front];
    }
}

2.7获取队尾元素

rear指向队尾元素的下一位置,所以逻辑上下标为rear-1的数据便是队尾元素。但是该队列是循环队列,所以rear可能指向下标为0处,所以应该先给rear+k,再减去1,便指向了队尾元素。但rear还可能指向其他位置,为了避免出错,给rear+k-1对k取余即可。

int myCircularQueueRear(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->data[obj->rear + obj->k] % (obj->k + 1);
    }
}

2.8销毁

void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->data);
    free(obj);
}

以上就是我对循环队列的理解,如有错误希望大家能够指正~

  • 23
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

今天学习了吗•

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

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

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

打赏作者

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

抵扣说明:

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

余额充值