【10.2】队列-设计循环队列

news2025/5/11 2:14:49

一、题目

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

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

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

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

示例:

MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3
circularQueue.enQueue(1);  // 返回 true
circularQueue.enQueue(2);  // 返回 true
circularQueue.enQueue(3);  // 返回 true
circularQueue.enQueue(4);  // 返回 false,队列已满
circularQueue.Rear();  // 返回 3
circularQueue.isFull();  // 返回 true
circularQueue.deQueue();  // 返回 true
circularQueue.enQueue(4);  // 返回 true
circularQueue.Rear();  // 返回 4

提示:

  • 所有的值都在 0 至 1000 的范围内;
  • 操作数将在 1 至 1000 的范围内;
  • 请不要使用内置的队列库。

二、解题思路

2.1 数组实现

        我们可以通过数组来模拟循环队列,利用数组的索引构建一个虚拟的环形结构。在循环队列中,设置两个指针:队尾 `rear` 和队首 `front`,队列的大小是固定的。结构如下图所示:

        在循环队列中,当队列为空时,`front` 和 `rear` 相等,即 `front == rear`;而当队列的所有空间都被占满时,同样会出现 `front == rear` 的情况。为了区分这两种状态,我们规定队列的数组容量为 `capacity`,但循环队列最多只能存储 `capacity - 1` 个元素。当队列中只剩下一个空闲的存储单元时,就认为队列已满。因此,队列判空的条件是 `front == rear`,而判满的条件是 `front == (rear + 1) % capacity`。

        对于一个固定大小的数组,只要知道队尾 `rear` 和队首 `front`,就可以计算出队列的当前长度:  (rear - front + capacity) mod capacity

循环队列的主要属性如下:

        **`elements`**:一个固定大小的数组,用于存储循环队列的元素。
        **`capacity`**:循环队列的容量,即队列中最多可以容纳的元素数量。
        **`front`**:队首元素在数组中的索引。
        **`rear`**:队尾元素的下一个位置的索引。

循环队列的接口方法如下:

        **`MyCircularQueue(int k)`**:初始化队列,数组的空间大小为 `k + 1`,并将 `front` 和 `rear` 初始化为 0。
        **`enQueue(int value)`**:在队列的尾部插入一个元素,并将 `rear` 更新为 `(rear + 1) % capacity`。
        **`deQueue()`**:从队首取出一个元素,并将 `front` 更新为 `(front + 1) % capacity`。
        **`Front()`**:返回队首的元素,需要先检测队列是否为空。
        **`Rear()`**:返回队尾的元素,需要先检测队列是否为空。
        **`isEmpty()`**:检测队列是否为空,只需判断 `rear` 是否等于 `front`。
        **`isFull()`**:检测队列是否已满,只需判断 `front` 是否等于 `(rear + 1) % capacity`。

        通过这种方式,循环队列能够高效地利用数组空间,同时避免普通队列在空间利用上的不足。

#include <iostream>
#include <vector>
using namespace std;

class MyCircularQueue {
private:
    int front;           // 队首指针
    int rear;            // 队尾指针
    int capacity;        // 队列容量
    vector<int> elements; // 用于存储队列元素的数组

public:
    // 构造函数,初始化队列
    MyCircularQueue(int k) {
        this->capacity = k + 1;          // 容量为 k + 1,多分配一个空间用于区分空和满状态
        this->elements = vector<int>(capacity); // 初始化数组
        rear = front = 0;                // 初始化队首和队尾指针
    }

    // 向循环队列插入一个元素
    bool enQueue(int value) {
        if (isFull()) {
            return false; // 队列已满,插入失败
        }
        elements[rear] = value;         // 将元素插入队尾
        rear = (rear + 1) % capacity;   // 更新队尾指针(循环)
        return true;
    }

    // 从循环队列中删除一个元素
    bool deQueue() {
        if (isEmpty()) {
            return false; // 队列为空,删除失败
        }
        front = (front + 1) % capacity; // 更新队首指针(循环)
        return true;
    }

    // 获取队首元素
    int Front() {
        if (isEmpty()) {
            return -1; // 队列为空,返回 -1
        }
        return elements[front]; // 返回队首元素
    }

    // 获取队尾元素
    int Rear() {
        if (isEmpty()) {
            return -1; // 队列为空,返回 -1
        }
        return elements[(rear - 1 + capacity) % capacity]; // 返回队尾元素(处理循环情况)
    }

    // 检查队列是否为空
    bool isEmpty() {
        return rear == front; // 队首和队尾指针相等时,队列为空
    }

    // 检查队列是否已满
    bool isFull() {
        return ((rear + 1) % capacity) == front; // 队尾的下一个位置是队首时,队列已满
    }
};

int main() {
    // 创建循环队列,容量为 3
    MyCircularQueue circularQueue(3);

    // 测试 enQueue 操作
    cout << "enQueue(1): " << circularQueue.enQueue(1) << endl; // 输出: 1 (true)
    cout << "enQueue(2): " << circularQueue.enQueue(2) << endl; // 输出: 1 (true)
    cout << "enQueue(3): " << circularQueue.enQueue(3) << endl; // 输出: 1 (true)
    cout << "enQueue(4): " << circularQueue.enQueue(4) << endl; // 输出: 0 (false,队列已满)

    // 测试 Rear 操作
    cout << "Rear(): " << circularQueue.Rear() << endl; // 输出: 3

    // 测试 isFull 操作
    cout << "isFull(): " << circularQueue.isFull() << endl; // 输出: 1 (true)

    // 测试 deQueue 操作
    cout << "deQueue(): " << circularQueue.deQueue() << endl; // 输出: 1 (true)

    // 测试 enQueue 操作
    cout << "enQueue(4): " << circularQueue.enQueue(4) << endl; // 输出: 1 (true)

    // 测试 Rear 操作
    cout << "Rear(): " << circularQueue.Rear() << endl; // 输出: 4

    // 测试 Front 操作
    cout << "Front(): " << circularQueue.Front() << endl; // 输出: 2

    // 测试 isEmpty 操作
    cout << "isEmpty(): " << circularQueue.isEmpty() << endl; // 输出: 0 (false)

    return 0;
}

复杂度分析

  • 时间复杂度:初始化和每项操作的时间复杂度均为 O(1)。

  • 空间复杂度:O(k),其中 k 为给定的队列元素数目。

 

2.2 链表实现

        我们也可以使用链表来实现队列。与数组相比,链表实现队列更加灵活,因为链表可以在 O(1)时间复杂度内完成元素的插入和删除操作。具体来说,入队操作是将新元素插入到链表的尾部,而出队操作则是返回链表的头节点,并将头节点指向下一个节点。

循环队列的属性如下:

  • head:链表的头节点,表示队列的头部。

  • tail:链表的尾节点,表示队列的尾部。

  • capacity:队列的容量,即队列可以存储的最大元素数量。

  • size:队列当前存储的元素数量。

        通过链表实现循环队列,可以避免数组实现中需要处理索引循环的问题,同时也能高效地完成队列的基本操作。

#include <iostream>
using namespace std;

// 链表节点定义
struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(nullptr) {}
};

class MyCircularQueue {
private:
    ListNode *head;  // 队首指针,指向链表的头节点
    ListNode *tail;  // 队尾指针,指向链表的尾节点
    int capacity;    // 队列的容量
    int size;        // 队列当前的大小

public:
    // 构造函数,初始化队列
    MyCircularQueue(int k) {
        this->capacity = k;  // 设置队列容量
        this->size = 0;      // 初始化队列大小为 0
        this->head = nullptr; // 初始化队首指针为空
        this->tail = nullptr; // 初始化队尾指针为空
    }

    // 向队列尾部插入一个元素
    bool enQueue(int value) {
        if (isFull()) {
            return false; // 队列已满,插入失败
        }
        ListNode *node = new ListNode(value); // 创建新节点
        if (!head) {
            head = tail = node; // 如果队列为空,新节点既是队首也是队尾
        } else {
            tail->next = node; // 将新节点链接到队尾
            tail = node;       // 更新队尾指针
        }
        size++; // 队列大小加 1
        return true;
    }

    // 从队列头部删除一个元素
    bool deQueue() {
        if (isEmpty()) {
            return false; // 队列为空,删除失败
        }
        ListNode *node = head; // 保存队首节点
        head = head->next;     // 更新队首指针
        size--;                // 队列大小减 1
        delete node;           // 释放队首节点的内存
        if (isEmpty()) {
            tail = nullptr; // 如果队列为空,更新队尾指针为空
        }
        return true;
    }

    // 获取队首元素
    int Front() {
        if (isEmpty()) {
            return -1; // 队列为空,返回 -1
        }
        return head->val; // 返回队首节点的值
    }

    // 获取队尾元素
    int Rear() {
        if (isEmpty()) {
            return -1; // 队列为空,返回 -1
        }
        return tail->val; // 返回队尾节点的值
    }

    // 检查队列是否为空
    bool isEmpty() {
        return size == 0; // 队列大小为 0 时为空
    }

    // 检查队列是否已满
    bool isFull() {
        return size == capacity; // 队列大小等于容量时为满
    }
};

int main() {
    // 创建循环队列,容量为 3
    MyCircularQueue circularQueue(3);

    // 测试 enQueue 操作
    cout << "enQueue(1): " << circularQueue.enQueue(1) << endl; // 输出: 1 (true)
    cout << "enQueue(2): " << circularQueue.enQueue(2) << endl; // 输出: 1 (true)
    cout << "enQueue(3): " << circularQueue.enQueue(3) << endl; // 输出: 1 (true)
    cout << "enQueue(4): " << circularQueue.enQueue(4) << endl; // 输出: 0 (false,队列已满)

    // 测试 Rear 操作
    cout << "Rear(): " << circularQueue.Rear() << endl; // 输出: 3

    // 测试 isFull 操作
    cout << "isFull(): " << circularQueue.isFull() << endl; // 输出: 1 (true)

    // 测试 deQueue 操作
    cout << "deQueue(): " << circularQueue.deQueue() << endl; // 输出: 1 (true)

    // 测试 enQueue 操作
    cout << "enQueue(4): " << circularQueue.enQueue(4) << endl; // 输出: 1 (true)

    // 测试 Rear 操作
    cout << "Rear(): " << circularQueue.Rear() << endl; // 输出: 4

    // 测试 Front 操作
    cout << "Front(): " << circularQueue.Front() << endl; // 输出: 2

    // 测试 isEmpty 操作
    cout << "isEmpty(): " << circularQueue.isEmpty() << endl; // 输出: 0 (false)

    return 0;
}

复杂度分析

  • 时间复杂度:初始化和每项操作的时间复杂度均为 O(1)。

  • 空间复杂度:O(k),其中 k 为给定的队列元素数目。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2284366.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多人-多agent协同可能会挑战维纳的反馈

在多人-多Agent协同系统中&#xff0c;维纳的经典反馈机制将面临新的挑战&#xff0c;而协同过程中的“算计”&#xff08;策略性决策与协调&#xff09;成为实现高效协作的核心。 1、非线性与动态性 维纳的反馈理论&#xff08;尤其是在控制理论中&#xff09;通常假设系统的动…

HarmonyOS简介:应用开发的机遇、挑战和趋势

问题 更多的智能设备并没有带来更好的全场景体验 连接步骤复杂数据难以互通生态无法共享能力难以协同 主要挑战 针对不同设备上的不同操作系统&#xff0c;重复开发&#xff0c;维护多套版本 多种语言栈&#xff0c;对人员技能要求高 多种开发框架&#xff0c;不同的编程…

Edge-TTS在广电系统中的语音合成技术的创新应用

Edge-TTS在广电系统中的语音合成技术的创新应用 作者&#xff1a;本人是一名县级融媒体中心的工程师&#xff0c;多年来一直坚持学习、提升自己。喜欢Python编程、人工智能、网络安全等多领域的技术。 摘要 随着人工智能技术的快速发展&#xff0c;文字转语音&#xff08;Te…

2025课题推荐——USBL与DVL数据融合的实时定位系统

准确的定位技术是现代海洋探测、海洋工程和水下机器人操作的基础。超短基线&#xff08;USBL&#xff09;和多普勒速度计&#xff08;DVL&#xff09;是常用的水下定位技术&#xff0c;但单一技术难以应对复杂环境。因此&#xff0c;USBL与DVL的数据融合以构建实时定位系统&…

RK3588平台开发系列讲解(ARM篇)ARM64底层中断处理

文章目录 一、异常级别二、异常分类2.1、同步异常2.2、异步异常三、中断向量表沉淀、分享、成长,让自己和他人都能有所收获!😄 一、异常级别 ARM64处理器确实定义了4个异常级别(Exception Levels, EL),分别是EL0到EL3。这些级别用于管理处理器的特权级别和权限,级别越高…

MyBatis最佳实践:提升数据库交互效率的秘密武器

第一章&#xff1a;框架的概述&#xff1a; MyBatis 框架的概述&#xff1a; MyBatis 是一个优秀的基于 Java 的持久框架&#xff0c;内部对 JDBC 做了封装&#xff0c;使开发者只需要关注 SQL 语句&#xff0c;而不关注 JDBC 的代码&#xff0c;使开发变得更加的简单MyBatis 通…

Three.js实战项目02:vue3+three.js实现汽车展厅项目

文章目录 实战项目02项目预览项目创建初始化项目模型加载与展厅灯光加载汽车模型设置灯光材质设置完整项目下载实战项目02 项目预览 完整项目效果: 项目创建 创建项目: pnpm create vue安装包: pnpm add three@0.153.0 pnpm add gsap初始化项目 修改App.js代码&#x…

1月27(信息差)

&#x1f30d;喜大普奔&#xff0c;适用于 VS Code 的 GitHub Copilot 全新免费版本正式推出&#xff0c;GitHub 全球开发者突破1.5亿 &#x1f384;Kimi深夜炸场&#xff1a;满血版多模态o1级推理模型&#xff01;OpenAI外全球首次&#xff01;Jim Fan&#xff1a;同天两款国…

开发环境搭建-3:配置 nodejs 开发环境 (fnm+ node + pnpm)

在 WSL 环境中配置&#xff1a;WSL2 (2.3.26.0) Oracle Linux 8.7 官方镜像 node 官网&#xff1a;https://nodejs.org/zh-cn/download 点击【下载】&#xff0c;选择想要的 node 版本、操作系统、node 版本管理器、npm包管理器 根据下面代码提示依次执行对应代码即可 基本概…

一个局域网通过NAT访问另一个地址重叠的局域网(IP方式访问)

正文共&#xff1a;1335 字 7 图&#xff0c;预估阅读时间&#xff1a;4 分钟 现在&#xff0c;我们已经可以通过调整两台设备的组合配置&#xff08;地址重叠时&#xff0c;用户如何通过NAT访问对端IP网络&#xff1f;&#xff09;或仅调整一台设备的配置&#xff08;仅操作一…

DeepSeek学术题目选择效果怎么样?

论文选题 一篇出色的论文背后&#xff0c;必定有一个“智慧的选题”在撑腰。选题足够好文章就能顺利登上高水平期刊&#xff1b;选题不行再精彩的写作也只能“当花瓶”。然而许多宝子们常常忽视这个环节&#xff0c;把大量时间花在写作上&#xff0c;选题时却像抓阄一样随便挑一…

正反转电路梯形图

1、正转联锁控制。按下正转按钮SB1→梯形图程序中的正转触点X000闭合→线圈Y000得电→Y000自锁触点闭合&#xff0c;Y000联锁触点断开&#xff0c;Y0端子与COM端子间的内部硬触点闭合→Y000自锁触点闭合&#xff0c;使线圈Y000在X000触点断开后仍可得电。 Y000联锁触点断开&…

高可用集群故障之join

本文记录了在部署高可用的k8s集群时&#xff0c;遇到的一个故障及其解决方法。 集群环境 描述&#xff1a;三主三从&#xff0c;eth0为外网网卡&#xff0c;eth1为内网网卡&#xff0c;内网互通。 需求&#xff1a;eth0只负责访问外网&#xff0c;eth1作为集群间的通信。 主…

【Web开发】一步一步详细分析使用Bolt.new生成的简单的VUE项目

https://bolt.new/ 这是一个bolt.new生成的Vue小项目&#xff0c;让我们来一步一步了解其架构&#xff0c;学习Vue开发&#xff0c;并美化它。 框架: Vue 3: 用于构建用户界面。 TypeScript: 提供类型安全和更好的开发体验。 Vite: 用于快速构建和开发 主界面如下&#xff1a…

SpringBoot源码解析(八):Bean工厂接口体系

SpringBoot源码系列文章 SpringBoot源码解析(一)&#xff1a;SpringApplication构造方法 SpringBoot源码解析(二)&#xff1a;引导上下文DefaultBootstrapContext SpringBoot源码解析(三)&#xff1a;启动开始阶段 SpringBoot源码解析(四)&#xff1a;解析应用参数args Sp…

论文阅读 AlphaFold 2

用AlphaFold进行非常精确的蛋白质结构的预测(AlphaFold2) 发表于2021年07月15日 NatureDOI: 10.1038/s41586-021-03819-2自然和科学杂志评选为2021年最重要的科学突破之一2021年AI在科学界最大的突破 前言 2020年11月30号, deepmind博客说AlphaFold解决了50年以来生物学的大挑…

计算机网络 (62)移动通信的展望

一、技术发展趋势 6G技术的崛起 内生智能&#xff1a;6G将强调自适应网络架构&#xff0c;通过AI驱动的智能算法提升通信能力。例如&#xff0c;基于生成式AI的6G内生智能架构将成为重要研究方向&#xff0c;实现低延迟、高效率的智能通信。信息编码与调制技术&#xff1a;新型…

探索与创新:DeepSeek R1与Ollama在深度研究中的应用

在当今信息爆炸的时代&#xff0c;获取和处理信息的能力变得至关重要。特别是在学术和研究领域&#xff0c;如何有效地进行深度研究是一个亟待解决的问题。最近&#xff0c;一个名为DeepSeek R1的模型结合Ollama平台提供了一种创新的解决方案。本文将分析并解构这一新兴的研究工…

mantisbt添加修改用户密码

文章目录 问题当前版本安装流程创建用户修改密码老的方式探索阶段 问题 不太好改密码啊。貌似必须要域名要发邮件。公司太穷&#xff0c;看不见的东西不关心&#xff0c;只能改源码了。 当前版本 当前mantisbt版本 2.27 php版本 7.4.3 安装流程 &#xff08;下面流程不是…

记录 | Docker的windows版安装

目录 前言一、1.1 打开“启用或关闭Windows功能”1.2 安装“WSL”方式1&#xff1a;命令行下载方式2&#xff1a;离线包下载 二、Docker Desktop更新时间 前言 参考文章&#xff1a;Windows Subsystem for Linux——解决WSL更新速度慢的方案 参考视频&#xff1a;一个视频解决D…