当前位置: 首页 > news >正文

【C++游记】栈vs队列vs优先级队列

枫の个人主页

你不能改变过去,但你可以改变未来

算法/C++/数据结构/C

Hello,这里是小枫。C语言与数据结构和算法初阶两个板块都更新完毕,我们继续来学习C++的内容呀。C++是接近底层有比较经典的语言,因此学习起来注定枯燥无味,西游记大家都看过吧~,我希望能带着大家一起跨过九九八十一难,降伏各类难题,学会C++,我会尽我所能,以通俗易懂、幽默风趣的方式带给大家形象生动的知识,也希望大家遇到困难不退缩,遇到难题不放弃,学习师徒四人的精神!!!故此得名【C++游记】

 话不多说,让我们一起进入今天的学习吧~~~  

1>> 栈的说明——Stack

1.1>> 栈的基本定义

栈是一种线性数据结构,其核心特性遵循 “后进先出(Last-In-First-Out,LIFO)” 原则——即最后加入栈的元素,会最先被取出。

可以类比现实中的“堆叠物品”(如叠盘子、堆书):只能从顶部添加新物品,也只能从顶部拿走物品,无法直接操作中间或底部的物品。

1.2>> 栈的核心术语

术语定义对应操作
栈顶(Top)栈中最顶端的元素,是唯一能被直接操作的位置插入、删除操作均在此处进行
栈底(Bottom)栈中最底端的元素,是最早加入栈的元素只有栈中所有元素被删除后,才能被操作
入栈(Push)向栈顶添加一个新元素的操作操作后,新元素成为新的栈顶
出栈(Pop)从栈顶删除并返回该元素的操作操作后,原栈顶的下一个元素成为新栈顶
判空(Is Empty)判断栈中是否没有任何元素的操作若栈空,出栈操作会触发“栈下溢(Underflow)”
判满(Is Full)判断栈是否达到最大容量的操作(仅固定大小栈需要)若栈满,入栈操作会触发“栈上溢(Overflow)”
查看栈顶(Peek)返回栈顶元素但不删除该元素的操作仅读取栈顶值,不改变栈的结构

1.3>> 栈的两种常见实现方式

栈的实现依赖于底层存储结构,主要分为“数组实现”和“链表实现”,两者各有优劣:

1.3.1>> 数组实现(顺序栈)

使用数组作为底层存储,通过一个“栈顶指针(top)”标记当前栈顶位置:

  • 初始化创建固定大小的数组,top 初始化为 -1(表示栈空)。
  • 入栈(Push):先判断栈是否满,若不满则 top 加 1,将元素存入 top 指向的数组位置。
  • 出栈(Pop):先判断栈是否空,若不空则取出 top 指向的元素,top 减 1。
  • 优点:元素访问速度快(数组随机访问特性),实现简单。
  • 缺点:容量固定,若需动态扩容需重新分配数组空间,可能浪费内存或触发上溢。

1.3.2>> 链表实现(链式栈)

使用链表作为底层存储,通常以“头节点”作为栈顶(无需遍历链表即可操作栈顶):

  • 初始化:链表头节点为 null(表示栈空)。
  • 入栈(Push):创建新节点,将新节点的 next 指向原头节点,再将头节点更新为新节点(新节点成为栈顶)。
  • 出栈(Pop):判断栈是否空,若不空则取出头节点的值,将头节点更新为原头节点的 next(原头节点的下一个节点成为新栈顶)。
  • 优点:容量动态变化,无需预先指定大小,不会触发上溢(理论上受内存限制)。
  • 缺点:每个元素需额外存储 next 指针,内存开销略大;访问速度不如数组(需通过指针遍历)。

2>> 队列的说明——Queue

2.1>> 队列的基本定义

队列是一种线性数据结构,其核心特性遵循 “先进先出(First-In-First-Out,FIFO)” 原则——即最早加入队列的元素,会最先被取出。

可以类比现实中的“排队”场景(如银行排队、超市结账):新的人从队尾加入,排在最前面的人先得到服务并离开队伍。

2.2>> 队列的核心术语

术语定义对应操作
队头(Front)队列中最前端的元素,是即将被取出的元素删除操作在此处进行
队尾(Rear)队列中最后端的元素,是最后加入队列的元素插入操作在此处进行
入队(Enqueue)向队尾添加一个新元素的操作操作后,新元素成为新的队尾
出队(Dequeue)从队头删除并返回该元素的操作操作后,原队头的下一个元素成为新队头
判空(Is Empty)判断队列中是否没有任何元素的操作若队空,出队操作会触发“队下溢”
判满(Is Full)判断队列是否达到最大容量的操作(仅固定大小队列需要)若队满,入队操作会触发“队上溢”
查看队头(Peek)返回队头元素但不删除该元素的操作仅读取队头值,不改变队列的结构

2.3>> 队列的常见实现方式

队列的实现主要有以下两种方式:

2.3.1>> 数组实现(顺序队列)

使用数组作为底层存储,通过两个指针(front 和 rear)分别标记队头和队尾:

  • 初始化:创建固定大小的数组,front 和 rear 初始化为 -1(表示队空)。
  • 入队(Enqueue):先判断队列是否满,若不满则 rear 加 1,将元素存入 rear 指向的数组位置。
  • 出队(Dequeue):先判断队列是否空,若不空则取出 front 指向的元素,front 加 1。
  • 循环队列优化:为解决普通顺序队列的"假溢出"问题,将数组视为环形,当 rear 到达数组末尾时,若前面有空位则绕回数组开头。
  • 优点:实现简单,元素访问速度快。
  • 缺点:容量固定,需处理假溢出问题。
2.3.2>> 链表实现(链式队列)

使用链表作为底层存储,通过头指针和尾指针分别指向队头和队尾:

  • 初始化:头指针和尾指针均为 null(表示队空)。
  • 入队(Enqueue):创建新节点,若队空则头、尾指针均指向新节点,否则将新节点链接到尾节点后,并更新尾指针。
  • 出队(Dequeue):判断队列是否空,若不空则取出头节点的值,更新头指针为原头节点的下一个节点,若队空则同时更新尾指针为 null。
  • 优点:容量动态变化,无需预先指定大小,不会有假溢出问题。
  • 缺点:每个元素需额外存储指针,内存开销略大。

3>> 优先级队列说明——Pirority_queue

3.1>> 优先级队列的基本定义

优先级队列是一种特殊的队列,它不遵循严格的 FIFO 原则,而是每次出队的是优先级最高的元素

可以类比现实中的“医院急诊”场景:急诊病人(高优先级)会比普通病人(低优先级)先得到治疗,而不管他们到达的顺序。

3.2>> 优先级队列的核心特性

  • 优先级:每个元素都有一个关联的优先级(通常是一个数值)。
  • 动态排序:元素按优先级排序,优先级最高的元素位于“队头”。
  • 灵活出队:出队操作始终移除优先级最高的元素,而非最早加入的元素。
  • 优先级规则:可以定义“数值越大优先级越高”或“数值越小优先级越高”,根据具体需求而定。

3.3>> 优先级队列的实现方式

优先级队列的常见实现方式有:

3.3.1>> 数组或链表实现
  • 入队操作:直接在数组或链表末尾插入元素,时间复杂度 O(1)。
  • 出队操作:遍历整个数组或链表,找到优先级最高的元素并删除,时间复杂度 O(n)。
  • 优点:实现简单。
  • 缺点:出队操作效率低,适用于数据量小的场景。
3.3.2>> 堆(Heap)实现

堆是实现优先级队列的高效数据结构,通常使用二叉堆:

  • 最大堆:父节点的优先级高于子节点,根节点是优先级最高的元素。
  • 最小堆:父节点的优先级低于子节点,根节点是优先级最低的元素。
  • 入队操作:将元素插入堆的末尾,然后“上浮”调整堆结构,时间复杂度 O(log n)。
  • 出队操作:移除根节点(优先级最高),将最后一个元素移到根节点位置,然后“下沉”调整堆结构,时间复杂度 O(log n)。
  • 优点:入队和出队操作效率高,是优先级队列的首选实现方式。
  • 缺点:实现相对复杂。

4>>三种结构模拟实现

4.1>>stack.h

#pragma once  // 防止头文件被多次包含#include<bits/stdc++.h>  // 包含标准库头文件
#include<deque>  // 包含deque容器的头文件namespace wc {  // 自定义命名空间,避免命名冲突// 栈类模板实现,使用容器适配器模式// T: 栈中元素的类型,Container: 底层容器类型,默认使用dequetemplate<class T, class Container = deque<T>>class stack{public:// 压栈操作:向栈顶添加元素void push(const T& x) {_con.push_back(x);  // 调用底层容器的尾插接口}// 出栈操作:移除栈顶元素void pop() {_con.pop_back();  // 调用底层容器的尾删接口}// 获取栈中元素个数size_t size()const {return _con.size();  // 调用底层容器的size接口}// 判断栈是否为空bool empty()const {return _con.empty();  // 调用底层容器的empty接口}// 获取栈顶元素(const版本,用于const对象)const T& top()const {return _con.back();  // 调用底层容器的back接口获取最后一个元素}// 获取栈顶元素(非const版本,用于非const对象)T& top() {return _con.back();  // 调用底层容器的back接口获取最后一个元素}private:Container _con;  // 底层容器对象,用于存储栈的元素};
}

4.2>>queue.h

#pragma once
#include<bits/stdc++.h>  // 包含标准库头文件,提供deque等容器支持namespace wc {  // 自定义命名空间,避免与标准库queue冲突// 模板参数:// T:队列中存储的元素类型// Container:底层容器类型,默认使用deque<T>(双端队列,兼顾头删和尾插效率)template<class T, class Container = deque<T>>class queue {public:// 向队列尾部插入元素// 借助底层容器的push_back实现,时间复杂度O(1)void push(const T& x) {_con.push_back(x);}// 移除队列头部的元素// 借助底层容器的pop_front实现,注意:不返回被移除的元素// 选择deque作为默认容器的原因:deque的pop_front效率高于vector(O(1) vs O(n))void pop() {_con.pop_front();}// 返回队列中元素的个数size_t size()const {return _con.size();}// 判断队列是否为空(无元素返回true)bool empty()const {return _con.empty();}// 获取队列头部元素的const引用(只读)const T& front()const {return _con.front();}// 获取队列头部元素的引用(可修改)T& front() {return _con.front();}// 获取队列尾部元素的const引用(只读)const T& back()const{return _con.back();}// 获取队列尾部元素的引用(可修改)T& back() {return _con.back();}private:Container _con;  // 底层容器对象,所有操作通过封装该容器实现};
}

4.3>>priority_queue.h

#pragma once
#include<bits/stdc++.h>  // 包含标准库头文件,提供容器基础支持// 定义less仿函数:用于比较两个元素,返回x < y的结果
// 作为默认比较方式,用于构建大顶堆(父节点大于子节点)
template<class T>
class less {
public:bool operator()(const T& x, const T& y) {return x < y;  // 当x小于y时返回true}
};// 定义Greater仿函数:用于比较两个元素,返回x > y的结果
// 用于构建小顶堆(父节点小于子节点)
template<class T>
class Greater {
public:bool operator()(const T& x, const T& y) {return x > y;  // 当x大于y时返回true}
};namespace wc {  // 自定义命名空间,避免与标准库冲突// 优先队列模板类// 模板参数:// T:存储的元素类型// Container:底层容器类型,默认使用vector(连续空间适合堆结构)// Compare:比较仿函数,默认使用Less<T>(构建大顶堆)template<class T, class Container = vector<T>, class Compare = less<T>> class priority_queue {public:// 默认构造函数:使用默认成员初始化(_con会调用vector的默认构造)priority_queue() = default;// 迭代器区间构造函数:用[first, last)区间的元素初始化优先队列template<class InputIterator>priority_queue(InputIterator first, InputIterator last):_con(first, last)  // 先用区间元素初始化底层容器{// 从最后一个非叶子节点开始,依次向下调整堆// 最后一个非叶子节点索引:(元素个数-1-1)/2for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--) {adjust_down(i);}}// 向优先队列插入元素void push(const T& x) {_con.push_back(x);  // 先将元素插入到底层容器尾部adjust_up(_con.size() - 1);  // 从新元素位置向上调整堆结构}// 删除优先队列的顶部元素(优先级最高的元素)void pop() {swap(_con[0], _con[_con.size() - 1]);  // 交换堆顶与最后一个元素_con.pop_back();  // 删除最后一个元素(原堆顶)adjust_down(0);  // 从堆顶位置向下调整堆结构}// 获取优先队列顶部元素(优先级最高的元素)const T& top() {return _con[0];  // 堆顶元素位于底层容器的第一个位置}// 判断优先队列是否为空bool empty()const {return _con.empty();  // 委托底层容器判断}// 获取优先队列中元素的个数size_t size()const {return _con.size();  // 委托底层容器获取}private:// 向上调整函数:用于插入元素后维持堆结构// 参数child:新插入元素的索引位置void adjust_up(int child) {Compare com;  // 创建比较仿函数对象int parent = (child - 1) / 2;  // 计算父节点索引// 当子节点索引大于0(未到达根节点)时循环while (child > 0) {// 若父节点不符合比较规则(需要交换)if (com(_con[parent], _con[child])) {  // 注意:原代码此处有笔误,应为_con[parent]swap(_con[child], _con[parent]);  // 交换父子节点child = parent;  // 更新子节点为原父节点parent = (child - 1) / 2;  // 重新计算父节点}else {break;  // 符合堆结构,停止调整}}}// 向下调整函数:用于删除元素后维持堆结构// 参数parent:需要调整的父节点索引void adjust_down(int parent) {Compare com;  // 创建比较仿函数对象int child = parent * 2 + 1;  // 计算左子节点索引// 当子节点索引小于容器大小(存在该子节点)时循环while (child < _con.size()) {// 若右子节点存在且右子节点更符合比较规则,则选择右子节点if (child + 1 < _con.size() && com(_con[child], _con[child + 1])) {child++;  // 切换到右子节点}// 若父节点不符合比较规则(需要交换)if (com(_con[parent], _con[child])) {  // 注意:原代码此处有笔误,应为_con[parent]swap(_con[child], _con[parent]);  // 交换父子节点parent = child;  // 更新父节点为原子节点child = parent * 2 + 1;  // 重新计算左子节点}else {break;  // 符合堆结构,停止调整}}}private:Container _con;  // 底层容器:用于存储元素,堆结构基于此容器实现};
}

5>>结语

今日C++到这里就结束啦,如果觉得文章还不错的话,可以三连支持一下。感兴趣的宝子们欢迎持续订阅小枫,小枫在这里谢谢宝子们啦~小枫の主页还有更多生动有趣的文章,欢迎宝子们去点评鸭~C++的学习很陡,时而巨难时而巨简单,希望宝子们和小枫一起坚持下去~你们的三连就是小枫的动力,感谢支持~

http://www.dtcms.com/a/352552.html

相关文章:

  • 算法编程实例-快乐学习
  • 随机森林实战:在鸢尾花数据集上与决策树和逻辑斯蒂回归进行对比
  • AI安全监控与人才需求的时间悖论(对AI安全模型、AI安全人才需求的一些思考)
  • AIDL和HIDL的AudioHal对比
  • Maya绑定基础: FK 和 IK 介绍和使用
  • lottie动画动态更改切图添加事件
  • 五自由度磁悬浮轴承:精准狙击转子质量不平衡引发的同频振动
  • pycharm 远程连接服务器报错
  • NeRAF、ImVid论文解读
  • 2007-2022年上市公司企业关联交易数据
  • 面向对象爬虫架构设计:构建高复用、抗封禁的爬虫系统​
  • 工业数据消费迎来“抖音式”革命:TDengine IDMP 让数据自己开口说话
  • 利用 Java 爬虫按关键字搜索 1688 商品详情 API 返回值说明实战指南
  • 如何在360极速浏览器中调出底部状态栏
  • Wireshark和USRP捕获同一信号波形差异原因
  • MQ 最终一致性实现跨库转账
  • ArcGIS学习-11 实战-商场选址
  • 【Vue3】Cesium实现雨雪效果
  • onnx入门教程(五)——实现 PyTorch-ONNX 精度对齐工具
  • 子串:和为K的子数组
  • 高并发内存池(7)- CentralCache的核心设计
  • 如何对springboot mapper 编写单元测试
  • MATLAB Figure画布中绘制表格详解
  • Cortex-M 的Thumb指令集?
  • k8s pod 启动失败 Failed to create pod sandbox
  • Il2CppInspector 工具linux编译使用
  • 算法概述篇
  • Markdown渲染引擎——js技能提升
  • MyBatis-Flex是如何避免不同数据库语法差异的?
  • 【electron】一、安装,打包配置