《数据结构》--队列【各种实现,算法推荐】
一、认识队列
队列是一种常见的数据结构,按照先进先出(FIFO,First In First Out)的原则排列数据。也就是说,最早进入队列的元素最先被移除。队列主要支持两种基本操作:
入队(enqueue):将元素添加到队列的尾部。
出队(dequeue):从队列的头部移除并返回元素。
队列的其他操作:
创建队列:初始化一个空队列。
查看队列头部元素:返回但不移除队列的头部元素(通常称为“peek”或“front”)。
判断队列是否为空:检查队列中是否还有元素。
获取队列大小:返回队列中元素的数量。
1.顺序队列
顺序队列即用顺序结构存储,数组实现:
#include <iostream> class Queue {
private: int* arr; int front; int rear; int capacity;
public: Queue(int size) { arr = new int[size]; capacity = size; front = 0; rear = -1; } ~Queue() { delete[] arr; } void enqueue(int item) { if (rear == capacity - 1) { std::cout << "Queue is full!" << std::endl; return; } arr[++rear] = item; } int dequeue() { if (is_empty()) { std::cout << "Queue is empty!" << std::endl; return -1; // 或抛出异常 } return arr[front++]; } int peek() { if (is_empty()) { std::cout << "Queue is empty!" << std::endl; return -1; // 或抛出异常 } return arr[front]; } bool is_empty() const { return front > rear; } int size() const { return rear - front + 1; }
}; int main() { Queue q(5); q.enqueue(10); q.enqueue(20); q.enqueue(30); std::cout << "Front element: " << q.peek() << std::endl; // 输出 10 std::cout << "Dequeued element: " << q.dequeue() << std::endl; // 输出 10 std::cout << "Queue size: " << q.size() << std::endl; // 输出 2 return 0;
}
2.链式队列
链式队列即用链式存储,用链表实现:
#include <iostream> class Node {
public: int data; Node* next; Node(int value) : data(value), next(nullptr) {}
}; class Queue {
private: Node* front; Node* rear; int size;
public: Queue() : front(nullptr), rear(nullptr), size(0) {} ~Queue() { while (!is_empty()) { dequeue(); } } void enqueue(int item) { Node* newNode = new Node(item); if (rear) { rear->next = newNode; } rear = newNode; if (!front) { front = rear; } size++; } int dequeue() { if (is_empty()) { std::cout << "Queue is empty!" << std::endl; return -1; // 或抛出异常 } Node* temp = front; int value = front->data; front = front->next; delete temp; if (!front) { rear = nullptr; // 队列变空 } size--; return value; } int peek() { if (is_empty()) { std::cout << "Queue is empty!" << std::endl; return -1; // 或抛出异常 } return front->data; } bool is_empty() const { return size == 0; } int get_size() const { return size; }
}; int main() { Queue q; q.enqueue(10); q.enqueue(20); q.enqueue(30); std::cout << "Front element: " << q.peek() << std::endl; // 输出 10 std::cout << "Dequeued element: " << q.dequeue() << std::endl; // 输出 10 std::cout << "Queue size: " << q.get_size() << std::endl; // 输出 2 return 0;
}
二、双端队列
1.顺序双端队列
#include <iostream> class Deque {
private: int* arr; int front; int rear; int capacity;
public: Deque(int size) { arr = new int[size]; capacity = size; front = -1; rear = 0; } ~Deque() { delete[] arr; } void insertFront(int item) { if (is_full()) { std::cout << "Deque is full!" << std::endl; return; } if (is_empty()) { front = 0; rear = 0; } else { front = (front - 1 + capacity) % capacity; // 循环移动前指针 } arr[front] = item; } void insertRear(int item) { if (is_full()) { std::cout << "Deque is full!" << std::endl; return; } if (is_empty()) { front = 0; rear = 0; } else { rear = (rear + 1) % capacity; // 循环移动后指针 } arr[rear] = item; } int deleteFront() { if (is_empty()) { std::cout << "Deque is empty!" << std::endl; return -1; // 或抛出异常 } int item = arr[front]; if (front == rear) { // 仅有一个元素 front = -1; rear = 0; } else { front = (front + 1) % capacity; // 循环移动前指针 } return item; } int deleteRear() { if (is_empty()) { std::cout << "Deque is empty!" << std::endl; return -1; // 或抛出异常 } int item = arr[rear]; if (front == rear) { // 仅有一个元素 front = -1; rear = 0; } else { rear = (rear - 1 + capacity) % capacity; // 循环移动后指针 } return item; } int getFront() const { if (is_empty()) { std::cout << "Deque is empty!" << std::endl; return -1; // 或抛出异常 } return arr[front]; } int getRear() const { if (is_empty()) { std::cout << "Deque is empty!" << std::endl; return -1; // 或抛出异常 } return arr[rear]; } bool is_empty() const { return front == -1; } bool is_full() const { return (rear + 1) % capacity == front; }
}; int main() { Deque dq(5); dq.insertRear(10); dq.insertRear(20); dq.insertFront(5); dq.insertFront(0); std::cout << "Front element: " << dq.getFront() << std::endl; // 输出 0 std::cout << "Rear element: " << dq.getRear() << std::endl; // 输出 20 dq.deleteFront(); // 删除 0 dq.deleteRear(); // 删除 20 std::cout << "Front element after deletions: " << dq.getFront() << std::endl; // 输出 5 return 0;
}
2.链式双端队列
双端队列的链式存储,采用双向链表来实现模拟:
#include <iostream> class Node {
public: int data; Node* next; Node* prev; Node(int value) : data(value), next(nullptr), prev(nullptr) {}
}; class Deque {
private: Node* front; Node* rear;
public: Deque() : front(nullptr), rear(nullptr) {} ~Deque() { while (!is_empty()) { deleteFront(); } } void insertFront(int item) { Node* newNode = new Node(item); if (is_empty()) { front = rear = newNode; } else { newNode->next = front; front->prev = newNode; front = newNode; } } void insertRear(int item) { Node* newNode = new Node(item); if (is_empty()) { front = rear = newNode; } else { rear->next = newNode; newNode->prev = rear; rear = newNode; } } int deleteFront() { if (is_empty()) { std::cout << "Deque is empty!" << std::endl; return -1; // 或抛出异常 } Node* temp = front; int value = front->data; front = front->next; if (front) { front->prev = nullptr; } else { rear = nullptr; // 如果队列变空 } delete temp; return value; } int deleteRear() { if (is_empty()) { std::cout << "Deque is empty!" << std::endl; return -1; // 或抛出异常 } Node* temp = rear; int value = rear->data; rear = rear->prev; if (rear) { rear->next = nullptr; } else { front = nullptr; // 如果队列变空 } delete temp; return value; } int getFront() const { if (is_empty()) { std::cout << "Deque is empty!" << std::endl; return -1; // 或抛出异常 } return front->data; } int getRear() const { if (is_empty()) { std::cout << "Deque is empty!" << std::endl; return -1; // 或抛出异常 } return rear->data; } bool is_empty() const { return front == nullptr; }
}; int main() { Deque dq; dq.insertRear(10); dq.insertRear(20); dq.insertFront(5); dq.insertFront(0); std::cout << "Front element: " << dq.getFront() << std::endl; // 输出 0 std::cout << "Rear element: " << dq.getRear() << std::endl; // 输出 20 dq.deleteFront(); // 删除 0 dq.deleteRear(); // 删除 20 std::cout << "Front element after deletions: " << dq.getFront() << std::endl; // 输出 5 return 0;
}
三、循环队列
1.顺序循环队列
#include <iostream> class CircularQueue {
private: int* arr; // 存储队列元素的数组 int front; // 队列头指针 int rear; // 队列尾指针 int capacity; // 队列的最大容量 int count; // 当前队列中的元素数量 public: CircularQueue(int size) { arr = new int[size]; capacity = size; front = 0; rear = -1; count = 0; } ~CircularQueue() { delete[] arr; } // 入队操作 void enqueue(int item) { if (is_full()) { std::cout << "Queue is full!" << std::endl; return; } rear = (rear + 1) % capacity; // 循环移动尾指针 arr[rear] = item; count++; } // 出队操作 int dequeue() { if (is_empty()) { std::cout << "Queue is empty!" << std::endl; return -1; // 或抛出异常 } int item = arr[front]; front = (front + 1) % capacity; // 循环移动头指针 count--; return item; } // 获取队头元素 int peek() const { if (is_empty()) { std::cout << "Queue is empty!" << std::endl; return -1; // 或抛出异常 } return arr[front]; } // 检查队列是否为空 bool is_empty() const { return count == 0; } // 检查队列是否已满 bool is_full() const { return count == capacity; } // 获取当前队列大小 int size() const { return count; }
}; int main() { CircularQueue cq(5); // 创建一个容量为5的循环队列 cq.enqueue(10); cq.enqueue(20); cq.enqueue(30); cq.enqueue(40); cq.enqueue(50); std::cout << "Front element: " << cq.peek() << std::endl; // 输出 10 std::cout << "Dequeued: " << cq.dequeue() << std::endl; // 输出 10 std::cout << "Front element after dequeue: " << cq.peek() << std::endl; // 输出 20 cq.enqueue(60); // 尝试入队一个新元素 std::cout << "Front element after enqueue: " << cq.peek() << std::endl; // 输出 20 return 0;
}
2.链式循环队列
采用循环链表的方式来实现循环队列
#include <iostream> class Node {
public: int data; Node* next; Node(int value) : data(value), next(nullptr) {}
}; class CircularQueue {
private: Node* front; // 队列头指针 Node* rear; // 队列尾指针 int count; // 当前队列中的元素数量 public: CircularQueue() : front(nullptr), rear(nullptr), count(0) {} ~CircularQueue() { while (!is_empty()) { dequeue(); } } // 入队操作 void enqueue(int item) { Node* newNode = new Node(item); if (is_empty()) { front = rear = newNode; rear->next = front; // 形成循环 } else { rear->next = newNode; rear = newNode; rear->next = front; // 形成循环 } count++; } // 出队操作 int dequeue() { if (is_empty()) { std::cout << "Queue is empty!" << std::endl; return -1; // 或抛出异常 } int item = front->data; Node* temp = front; if (front == rear) { // 只有一个元素 front = rear = nullptr; } else { front = front->next; rear->next = front; // 更新尾指针 } delete temp; count--; return item; } // 获取队头元素 int peek() const { if (is_empty()) { std::cout << "Queue is empty!" << std::endl; return -1; // 或抛出异常 } return front->data; } // 检查队列是否为空 bool is_empty() const { return count == 0; } // 获取当前队列大小 int size() const { return count; }
}; int main() { CircularQueue cq; // 创建一个循环队列 cq.enqueue(10); cq.enqueue(20); cq.enqueue(30); std::cout << "Front element: " << cq.peek() << std::endl; // 输出 10 std::cout << "Dequeued: " << cq.dequeue() << std::endl; // 输出 10 std::cout << "Front element after dequeue: " << cq.peek() << std::endl; // 输出 20 cq.enqueue(40); // 尝试入队一个新元素 std::cout << "Front element after enqueue: " << cq.peek() << std::endl; // 输出 20 return 0;
}
四、算法专题
队列是一种重要的数据结构,广泛应用于计算机科学的各个领域。理解队列的基本概念、操作和实现方式对于学习数据结构和算法非常重要。
1.队列的应用场景
任务调度:操作系统中的进程调度。
打印队列:管理打印任务的顺序。
广度优先搜索(BFS):图算法中的节点访问顺序。
消息队列:在分布式系统中传递消息。
2.队列的相关算法
循环队列:通过循环数组或链表实现,避免了空间浪费。
优先队列:每个元素都有一个优先级,出队时优先级高的元素先被移除。
阻塞队列:在多线程环境中使用,支持线程安全的入队和出队操作。
3.算法题推荐
模拟队列:232. 用栈实现队列 - 力扣(LeetCode)
class MyQueue {stack<int> v;
public:MyQueue() {}void push(int x) {v.push(x);}int pop() {if(empty())return NULL;stack<int> ts;while (!v.empty()) {ts.push(v.top());v.pop();}int ans = ts.top();ts.pop();while (!ts.empty()) {v.push(ts.top());ts.pop();}return ans;}int peek() {if(empty())return NULL;stack<int> ts = v;while (ts.size() != 1) {ts.pop();}//就剩栈底了return ts.top();}bool empty() {return v.empty();}
};/*** Your MyQueue object will be instantiated and called as such:* MyQueue* obj = new MyQueue();* obj->push(x);* int param_2 = obj->pop();* int param_3 = obj->peek();* bool param_4 = obj->empty();*/
239. 滑动窗口最大值 - 力扣(LeetCode)双端队列:239. 滑动窗口最大值 - 力扣(LeetCode)
class Solution {
public://时间复杂度:O((n-k)*k)vector<int> maxSlidingWindow(vector<int>& nums, int k) {vector<int> ret;if (nums.size() == 0)return ret;deque<int> q; //存储位置for (int i = 0; i < nums.size(); i++) {while (!q.empty() && nums[i] > nums[q.back()]) {q.pop_back();}q.push_back(i);if (k == i - q.front()) q.pop_front();//超出长度if (i >= k - 1) ret.push_back(nums[q.front()]);}return ret;}
};
优先队列(堆):23. 合并 K 个升序链表 - 力扣(LeetCode)
class Solution {
public:struct Status {int val;ListNode *ptr;bool operator < (const Status &rhs) const {return val > rhs.val;}};priority_queue <Status> q;ListNode* mergeKLists(vector<ListNode*>& lists) {for (auto node: lists) {if (node) q.push({node->val, node});}ListNode head, *tail = &head;while (!q.empty()) {auto f = q.top(); q.pop();tail->next = f.ptr; tail = tail->next;if (f.ptr->next) q.push({f.ptr->next->val, f.ptr->next});}return head.next;}
};
感谢大家!
相关文章:
《数据结构》--队列【各种实现,算法推荐】
一、认识队列 队列是一种常见的数据结构,按照先进先出(FIFO,First In First Out)的原则排列数据。也就是说,最早进入队列的元素最先被移除。队列主要支持两种基本操作: 入队(enqueue࿰…...
面试八股文对校招的用处有多大?--GDB篇
前言 1.本系列面试八股文的题目及答案均来自于网络平台的内容整理,对其进行了归类整理,在格式和内容上或许会存在一定错误,大家自行理解。内容涵盖部分若有侵权部分,请后台联系,及时删除。 2.本系列发布内容分为12篇…...
Unity用VS打开FGUI脚本变成杂项怎么处理?
在Unity中使用Visual Studio(VS)打开FGUI脚本时,如果脚本显示为杂项文件,这通常意味着VS没有正确识别或关联这些脚本文件。以下是一些解决此问题的步骤: 对惹,这里有一个游戏开发交流小组,大家…...
交叉熵损失函数(Cross-Entropy Loss Function)解释说明
公式 8-11 的内容如下: L ( y , a ) − [ y log a ( 1 − y ) log ( 1 − a ) ] L(y, a) -[y \log a (1 - y) \log (1 - a)] L(y,a)−[yloga(1−y)log(1−a)] 这个公式表示的是交叉熵损失函数(Cross-Entropy Loss Function)&#…...
和外部机构API交互如何防止外部机构服务不可用拖垮调用服务
引言 在现代的分布式系统和微服务架构中,服务之间的通信往往通过API进行,尤其是在与外部机构或第三方服务进行交互时,更需要通过API实现功能的集成。然而,由于外部服务的可控性较差,其服务的不可用性(如响…...
自动猫砂盆真的有必要吗?买自动猫砂盆不看这四点小心害死猫。
现在越来越多铲屎官选择购买自动猫砂盆来代替自己给猫咪铲屎,可是自动猫砂盆真的有必要吗?要知道,在现在忙碌的生活中,有很多人因为工作上的忙碌而不小心忽视了猫咪,猫咪的猫砂盆堆满粪便,要知道猫砂盆一天…...
国外解压视频素材哪里找?五个海外解压视频素材网站推荐
国外解压视频素材哪里找?五个海外解压视频素材网站推荐 如果你正在寻找国外的解压视频素材,那么今天这篇文章一定能帮助你。无论是修牛蹄、洗地毯,还是切肥皂、玩解压游戏等,下面分享的几个网站都是你找到高质量海外解压视频素材…...
Android一个APP里面最少有几个线程
Android一个APP里面最少有几个线程 参考 https://www.jianshu.com/p/92bff8d6282f https://www.jianshu.com/p/8a820d93c6aa 线程查看 Android一个进程里面最少包含5个线程,分别为: main线程(主线程)FinalizerDaemon线程 终结者守护线程…...
位操作解决数组的花样遍历
文章目录 题目 一、思路: 二、代码 总结 题目 leetcodeT289 https://leetcode.cn/problems/game-of-life/description/ 一、思路: 这题思路很简单,对每个位置按照题目所给规则进行遍历,判断周围网格的活细胞数即可。但是题目要求…...
【面试宝典】深入Python高级:直戳痛点的题目演示(下)
目录 🍔 Python下多线程的限制以及多进程中传递参数的⽅式 🍔 Python是如何进⾏内存管理的? 🍔 Python⾥⾯如何拷⻉⼀个对象? 🍔 Python⾥⾯search()和match()的区别? 🍔 lambd…...
Hive数仓操作(十七)
一、Hive的存储 一、Hive 四种存储格式 在 Hive 中,支持四种主要的数据存储格式,每种格式有其特点和适用场景,不过一般只会使用Text 和 ORC : 1. Text 说明:Hive 的默认存储格式。存储方式:行存储。优点…...
工业和自动化领域常见的通信协议
在工业和自动化领域,有多种常见的通信协议,主要用于设备间的通信、数据传输和控制。 Modbus: 类型:串行通信协议用途:广泛用于工业自动化设备间的通信,如PLC、传感器和执行器。优点:简单、开放且…...
连夜爆肝收藏各大云服务新老用户优惠活动入口地址(内含免费试用1个月的地址),适用于小白,大学生,开发者,小企业老板....
具体请前往:云服务器优惠活动入口大全--收藏各主流云厂商的云服务器等系列产品的优惠活动入口,免费试用1个月活动入口,让新老用户都能根据使用场景和身份快速锁定优惠权益 经济下滑,被优化增多,大学生就业难࿰…...
SpringBoot+Redis+RabbitMQ完成增删改查
各部分分工职责 RabbitMQ负责添加、修改、删除的异步操作 Redis负责数据的缓存 RabbitMQ里面角色职责简单描述 RabbitMQ里面有几个角色要先分清以及他们的对应关系: 交换机、队列、路由键 交换机和队列是一对多 队列和路由键是多对多 然后就是消息的发送者&…...
【系统集成中级】线上直播平台开发项目质量管理案例分析
【系统集成中级】线上直播平台开发项目质量管理案例分析 一、案例二、小林在项目质量管理中存在的问题(一)计划阶段缺失(二)测试用例编制与执行问题(三)质量管理流程问题(四)质量保证…...
浪潮信息领航边缘计算,推动AI与各行业深度融合
在9月20日于安徽盛大召开的浪潮信息边缘计算合作伙伴大会上,浪潮信息指出,未来的计算领域将全面融入AI技术,特别是在企业边缘侧,智能应用特别是生成式人工智能应用正在迅速普及,这一趋势正引领边缘计算向边缘智算的方向…...
Koa2项目实战3 (koa-body,用于处理 HTTP 请求中的请求体)
以用户注册接口为例,需要在请求里携带2个参数:用户名(user_name)和密码(password)。 开发者需要在接口端,解析出user_name 、password。 在使用Koa开发的接口中,如何解析出请求携带…...
复盘20241012
1、 classpath "com.android.tools.build:gradle:8.5.1" 的版本 与distributionUrlhttps\://services.gradle.org/distributions/gradle-8.9-bin.zip的对应规则: Execution failed for task :app:compileDebugKotlin. 解决方案 切换 setting --> ot…...
泊松流负载均衡控制
目录 泊松流负载均衡控制 一、到达率λ 二、服务率μ 三、泊松流负载均衡控制 泊松流负载均衡控制 在探讨泊松流负载均衡控制时,我们主要关注的是到达率λ和服务率μ这两个核心参数。以下是对这两个参数及其在泊松流负载均衡控制中作用的详细解释: 一、到达率λ 定义:…...
3D打印矫形器市场报告:未来几年年复合增长率CAGR为10.8%
3D 打印矫形器是指使用 3D 打印技术制作的定制外部支撑装置。它们有助于稳定、引导、缓解或纠正肌肉骨骼状况,并根据个体患者的解剖结构进行设计,通常使用 3D 扫描和建模技术。3D 打印在矫形器方面的主要优势是能够生产精确适合患者解剖结构的定制装置&a…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
