首页 > 其他分享 >【10.2】队列-设计循环队列

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

时间:2025-01-23 11:27:18浏览次数:3  
标签:队尾 10.2 capacity 队列 循环 front return rear

一、题目

        设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 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 为给定的队列元素数目。

标签:队尾,10.2,capacity,队列,循环,front,return,rear
From: https://blog.csdn.net/linshantang/article/details/145280654

相关文章

  • 回调函数 事件处理 dotnet .net 有界队列 背压机制(Backpressure)有界队列
    回调函数事件处理dotnet.net有界队列背压机制(Backpressure)有界队列通过有界队列来实现背压,确保生产者不会以超过消费者处理能力的速度发送数据。usingSystem.Threading.Channels;publicclassProgram{staticasyncTaskMain(string[]args){//创......
  • Linux事件循环
    在Linux中,事件循环是一种编程模式,通常用于处理并发事件或异步操作。它的核心思想是,程序在一个主循环中不断检查事件队列,处理这些事件并执行相应的操作,而不是阻塞等待每个操作完成。事件循环在很多高性能网络服务器和异步I/O框架中得到了广泛应用。事件循环的基本原理:事件检测:事......
  • 关于RNN (循环神经网络)相邻采样为什么在每次迭代之前都需要将参数detach
    转自:https://www.cnblogs.com/catnofishing/p/13287322.htmldetach到底有什么作用呢首先要明确一个意识:pytorch是动态计算图,每次backward后,本次计算图自动销毁,但是计算图中的节点都还保留。​方向传播直到叶子节点为止,否者一直传播,直到找到叶子节点我的答案是有用,但根本不......
  • C语言的循环结构
    循环结构是编程语言中的一种重要结构,用于重复执行一段代码。主要有三种循环结构:for循环,while循环和do-while循环。循环结构(1)当型循环结构:当条件P成立(为真)时,反复执行循环语句,直到条件P不成立(为假)时结束循环。(条件成立,才执行循环语句,for、while)(2)直到型循环结构:先......
  • 消息队列篇--原理篇--Pulsar(Namespace,BookKeeper,类似Kafka甚至更好的消息队列)
    ApachePulusar是一个分布式、多租户、高性能的发布/订阅(Pub/Sub)消息系统,最初由Yahoo开发并开源。它结合了Kafka和传统消息队列的优点,提供高吞吐量、低延迟、强一致性和可扩展的消息传递能力,适用于大规模分布式系统的实时数据处理和异步通信。Pulsar的架构设计结合了消息队......
  • 消息队列篇--原理篇--RabbitMQ和Kafka对比分析
    RabbitMQ和Kafka是两种非常流行的消息队列系统,但它们的设计哲学、架构特点和适用场景存在显著差异。对比如下。1、架构设计RabbitMQ:基AMQP协议:RabbitMQ是基于AMQP(高级消息队列协议)构建的,支持多种消息传递模式,如发布/订阅、路由、RPC等。单片架构:RabbitMQ采用的是传统的Br......
  • 数据结构-单向不带头不循环链表
    链表知识总结逻辑结构:线性结构(元素之间存在一对一关系)存储结构(物理结构):链式存储(存储顺序和逻辑顺序不在乎是否一致)1.链表的特点:擅长进行动态删除和增加操作,不擅长随机访问(需要遍历,因为链表不按顺序存放)2.链表分类:单双向链表单链表:元素节点有两部分组成(数据域-存储当前......
  • Day 8 循环结构
    1.while循环结构while(布尔表达式){   //循环内容}大多数情况下循环需要停止,我们需要一个让表达式失效的方式来结束循环。循环条件一直为true会造成无限循环“死循环”,正常的业务编程应尽量避免死循环,死循环会影响程序性能或者造成程序卡死崩溃。少部分情况需要循......
  • Blazor 循环的迷思
    Steps列表有9条记录,循环9次总是出错,写i<8就没问题,百思不得其解 然后改成这样,循环steps.count-1次,然后第最后一个元素再copy一次html,就不报错然后网友提示for里面要临时变量,for里面要临时变量,varindex=i,或者foreach   ......
  • 条件判断与循环
    条件判断与循环​​有符号的数跟无符号的数比大小的话,会把有符号的数也转化为无符号的数,这个时候的结果可能就会有偏差算法:可以整一个数来记录这个步骤怎么样了(eg:num++)(用于记录满足几个条件)(也就是计数器作用)‍eg:%4d在printf和scanf中的区分:在printf中:4表示如果......