循环队列详解

简介: 循环队列详解

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来判断队列是否已满。

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

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

1.3.4 入队

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

情况一:

情况二:

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 出队

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

情况一:

情况二:

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 练习

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

相关文章
|
存储 算法 编译器
【数据结构原理】稀疏矩阵 - THE SPARSE MATRIX
【数据结构原理】稀疏矩阵 - THE SPARSE MATRIX
476 0
|
自然语言处理 JavaScript 前端开发
《深度剖析:开发鸿蒙原生应用,为何ArkTS是最优之选》
ArkTS 是鸿蒙原生应用开发的核心语言,基于 TypeScript 深度扩展,具备强大的静态检查和类型系统,有效提升代码稳定性。其声明式语法简洁高效,助力快速构建复杂用户界面;多维度状态管理机制灵活掌控应用状态,支持全局与跨设备数据同步。此外,ArkTS 与 ArkUI 深度集成,优化分布式场景下的多设备协同开发体验,并通过完善工具链降低开发门槛。随着持续演进,ArkTS 将进一步推动鸿蒙生态繁荣,为开发者带来更高效的解决方案。
506 0
|
9月前
|
Windows
缺少文件X3DAudio1_7.dll问题解决!找不到x3daudio1_7.dll怎么解决?
当电脑提示“找不到X3DAudio1_7.dll”或“X3DAudio1_7.dll缺失”时,可能是DirectX版本不兼容所致。该DLL文件负责游戏音效传输,缺失会导致游戏声音异常。解决方法包括:下载缺失的DLL文件、使用DLL修复工具(如DirectxRepair一键修复)、安装游戏常用运行库合集。部分安装问题可通过关闭杀毒软件、使用管理员账户操作解决。
6147 0
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
902 6
|
监控 Java API
JDK动态代理和CGLIB动态代理
Java动态代理允许在运行时创建代理对象,增强或拦截目标类方法的执行。主要通过两种方式实现:JDK动态代理和CGLIB动态代理。JDK动态代理基于接口,利用`java.lang.reflect.Proxy`类和`InvocationHandler`接口;CGLIB则通过字节码技术生成目标类的子类作为代理,适用于未实现接口的类。两者均用于在方法执行前后添加额外逻辑,如日志记录、权限控制等,广泛应用于AOP框架中。
472 2
|
机器学习/深度学习 人工智能 自然语言处理
一文讲懂大模型推理技术细节
本文介绍了大模型推理在自然语言处理(NLP)领域的原理与应用。大模型推理利用如GPT、BERT等预训练模型,通过深度学习中的Transformer结构和自注意力机制,实现文本分类、情感分析等多种任务。文章提供了使用Hugging Face的Transformers库进行文本分类的示例代码,并展望了大模型推理技术未来的发展潜力。
|
SQL 安全 数据库
Python Web开发者必看!SQL注入、XSS、CSRF全面解析,守护你的网站安全!
在Python Web开发中,构建安全应用至关重要。本文通过问答形式,详细解析了三种常见Web安全威胁——SQL注入、XSS和CSRF,并提供了实用的防御策略及示例代码。针对SQL注入,建议使用参数化查询;对于XSS,需对输出进行HTML编码;而防范CSRF,则应利用CSRF令牌。通过这些措施,帮助开发者有效提升应用安全性,确保网站稳定运行。
377 1
|
运维 监控 算法
JDK 21中的分代ZGC:内存管理的革命性进步
本文深入探讨了JDK 21中引入的分代ZGC(Z Garbage Collector)的工作原理、特性及其对现代应用程序性能的影响。分代ZGC是一种基于分代收集的垃圾回收器,通过优化内存分配和回收过程,实现了更高的吞吐量和更低的延迟。本文将分析分代ZGC的设计哲学、技术细节以及在实际应用中的优势,并展示如何通过配置和优化分代ZGC来提升Java应用程序的性能。
1699 7
|
JavaScript 前端开发 开发者
javascript事件大全
javascript事件大全
273 1
更相减损术求最大公约数
更相减损术求最大公约数

热门文章

最新文章