用数组模拟循环队列

设计一种循环队列,线性数据结构,其操作表现为 FIFO(先进先出)原则且队尾被连接在队首之后形成一个循环,称作"环形缓冲器"

循环队列的好处是可以利用这个队列之前使用过的空间,但是他的空间大小是固定的

循环队列我们使用单链表或者数组都能实现,这里先讨论使用数组的方法

数组不像单链表一样可以直接做到循环,需要进行一定的操作

由 head 和 tail 在之间跳动实现了数据的出入与循环,有限的空间保证先进先出重复使用

tail 指向队尾数据的下一个空间

现在我们来看一下什么时候循环链表为空,什么时候循环链表为满呢?

根据前面的情况,当 head == tail 的时候队列为空

那什么时候满呢?

如此看来如果说空和满的情况都是一样的,那肯定不行。所以我们得换一种判断方式

解决方案一: 增加一个 size 来记录空还是满, size = 0 则为空, size = k 则为满,那size = k 时,tail 的前一个就是尾, head 就是头

解决方法二: 额外多开一个空间(官方推荐)

当 head == tail 的时候为空, 当 tail + 1 % (k + 1) == head 就是满

现在我们开始对框架进行构建
循环队列需要实现以下几个接口

  1. 初始化
  2. 判空
  3. 判满
  4. 插入数据,失败返回false
  5. 删除数据,失败返回false
  6. 取队头数据
  7. 取队尾数据
  8. 销毁该队列

初始化

我们先创建一下这个队列的结构体

cpp 复制代码
typedef struct {
	int* a;
	int head;  //指向头
	int tail;   //指向尾的下一个
	int k;
}MyCircularQueue;

一共有四个成员,一个数组用于存放数据,head 指向头, tail 指向尾, k 是该队列能存放的最大数据个数

cpp 复制代码
MyCircularQueue* MyCircularQueueCreate(int k)
{
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //防止假溢出问题,增加一个空间来判断空与满
    obj->a = (int*)malloc(sizeof(int) * (k + 1));
    obj->head = 0;
    obj->tail = 0;
    obj->k = k;
}

我们采用之前说的解决方案二,增加一个空间

判空

cpp 复制代码
bool MyCircularQueueIsEmpty(MyCircularQueue* obj)
{
    return obj->head == obj->tail;    
}

判满

cpp 复制代码
//判满
bool MyCircularQueueIsFull(MyCircularQueue* obj)
{
    return (obj->tail + 1) % (obj->k + 1) == obj->head;
}

当我们有数字循环的时候比如 0 1 2 3 0 1 2 3 这样,多用取模来处理数据

用大于自己的数来模自己还是本身,用这个特性,我们可以轻松化解循环的数字问题

插入一个数据,成功就返回 true 失败就返回 false

cpp 复制代码
bool MyCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
    if (MyCircularQueueIsFull(obj))
        return false;
    obj->a[obj->tail] = value;
    obj->tail++;
    obj->tail %= (obj->k + 1);
    return true;
}

我们都知道插入完一个数据 tail 往后走一格,当到了数组的最后的时候 tail 要跳到最前面去,所以不能一味的加加

要解决的情况是当 tail 小于 k 时 ,tail++,当 tail == k 的时候,不能再加加而是变成 0 ,这个时候取模又能直接完成这件事

obj->tail %= (obj->k + 1) 与前面判满的时候取模原理一样,不再过多赘述

删除一个数据,失败返回false

cpp 复制代码
bool MyCircularQueueDeQueue(MyCircularQueue* obj)
{
    if (MyCircularQueueIsEmpty(obj))
        return false;
    obj->head++;
    obj->head %= (obj->k + 1);
    return true;
}

删除数据的时候不可以直接把需要删除数据的空间给 free 掉, malloc 开辟额一连串的数据不可以分开进行销毁

返回队首的值

cpp 复制代码
int MyCircularQueueFront(MyCircularQueue* obj)
{
    if (MyCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->a[obj->head];
}

返回队尾的值

cpp 复制代码
int MyCircularQueueRear(MyCircularQueue* obj)
{
    if (MyCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->tail == 0 ? obj->a[obj->k] : obj->a[obj->tail - 1];
      //return obj->a[(obj->tail - 1 + obj->k + 1) % (obj->k + 1)];  这样写更装逼
}

这里依然要注意当 tail 为 0 的时候,不能再减一了,而是要跳到队尾去

队列的销毁

cpp 复制代码
//队列的销毁
void MyCircularQueueFree(MyCircularQueue* obj)
{
    free(obj->a);
    free(obj);
}

这里的数组和结构体是嵌套的,所以不能直接销毁了结构体就不管了,要先将 obj 中的数组销毁掉再销毁结构体

插一嘴,如果用单链表来代替数组的话,想要取尾数据就会比较的复杂,各有利弊,两者都需要思考思考才能解决

相关推荐
圣保罗的大教堂2 小时前
《算法笔记》9.3小节——数据结构专题(2)->树的遍历 问题 A: 树查找
算法
乌云暮年4 小时前
算法刷题整理合集(四)
java·开发语言·算法·dfs·bfs
Luo_LA5 小时前
【排序算法对比】快速排序、归并排序、堆排序
java·算法·排序算法
AI技术控5 小时前
机器学习算法实战——天气数据分析(主页有源码)
算法·机器学习·数据分析
oioihoii5 小时前
C++20 中的同步输出流:`std::basic_osyncstream` 深入解析与应用实践
c++·算法·c++20
猪猪成5 小时前
【图论】FLOYD弗洛伊德算法-最短路径
学习·算法·图论
HR Zhou5 小时前
群体智能优化算法-粒子群优化算法(Particle Swarm Optimization, PSO,含Matlab源代码)
算法·matlab·优化·智能优化算法
SylviaW085 小时前
python-leetcode 48.括号生成
算法·leetcode·职场和发展
飞奔的马里奥6 小时前
力扣算法Hot100——75. 颜色分类
java·算法·leetcode
Chen--Xing6 小时前
洛谷 P3986 斐波那契数列
python·算法