深入刨析C++ stl::stack 、stl::queue 及 priority_queue

深入刨析C++ stl::stack 、stl::queue 及 priority_queue

                        欢迎来到干货小仓库!!!

“Stack Overflow 教会我: 提问不可耻,放弃搜索才是”


1.stack的介绍和使用

1.1stack的介绍

1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。3. stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:empty:判空操作back:获取尾部元素操作push_back:尾部插入元素操作pop_back:尾部删除元素操作4. 标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器,默认情况下使用deque。

遵循 “先进后出” 原则。

                                ​​​​​​​        

1.2stack的使用

函数说明接口说明
stack()构造空的栈
empty()检测stack是否为空
size()返回stack中元素的个数
top()返回栈顶元素
push()将元素 val 压入stack中
pop()将 stack中 尾部的元素弹出

2.queue的介绍和使用

2.1queue的介绍

1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素。2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:empty:检测队列是否为空size:返回队列中有效元素的个数front:返回队头元素的引用back:返回队尾元素的引用push_back:在队列尾部入队列pop_front:在队列头部出队列4. 标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque。

遵循"先进先出"原则。

2.2queue的使用

函数声明接口说明
queue()构造空的队列
empty()检测队列是否为空,是返回true,否则返回false
size()返回队列中的元素个数
front()返回队头元素的引用
back()返回队尾元素的引用
push()在队尾将元素val入队列
pop()在队头元素出队列

3.priority_queue的介绍和使用

3.1介绍

1.底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:empty():检测容器是否为空size():返回容器中有效元素个数top():返回容器中第一个元素的引用push():在容器插入元素pop():删除容器元素2.标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。

3.2使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构。

因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆。

函数声明接口说明
priority_queue()构造一个空的优先级队列
priority_queue(InputIterator first,InputIterator last)迭代器初始化
empty()检测优先级队列是否为空,是返回true,否则返回false
top()
返回优先级队列中最大(最小元素),即堆顶元素
push(const T& val)在优先级队列中插入val元素
pop()
删除优先级队列中最大(最小)元素,即堆顶元素

4.容器适配器

通过字面理解,所谓的 适配器  就相当于我们在给我们手机充电的时候,电压是220V,难道往我们手机直接充电就是 220W的电压,很显然这不符合我们所理解的常识,我们的充电器的插头就充当了这个角色("适配器").

适配器是一种设计模式(设计模式是一套反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另一个接口。

template<class T,class Containter = deque<T>> class stack { //...... private: Containter _con; };

直观的理解就是 一个模板参数,这个模板参数是一个容器(可以是vector、list.....),作为成员变量的类型,根据使用者传的类型,作为成员变量的类型。

为什么stack和queue在STL中并没有将其划分在容器的队列,而是将其称为 "容器适配器"?



这是因为stack 和 queue 只是对其他容器的接口进行了包装。STL中stack 和queue的 默认适配器使用的是 deque。(下面有解析).

5.双端队列(deque)

5.1deque的原理介绍

deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。

注意:deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组。底层原理:

优缺点:

deque缺陷:①中间插入和删除.②频繁的访问(operator[]).双端队列的迭代器底层实现

5.2为什么选择deque作为stack和queue的底层默认容器?

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:



1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。

2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。



结合了deque的优点,而完美的避开了其缺陷。

6.Stack的模拟实现

template<class T, class Container = deque<T>> class stack { public: void push(const T& x) { _con.push_back(x); } void pop() { _con.pop_back(); } T& top() { return _con.back(); } size_t size() { return _con.size(); } bool empty() { return _con.empty(); } private: Container _con; };

7.queue的模拟实现

template<class T, class Container = deque<T>> class queue { public: void push(const T& x) { _con.push_back(x); } void pop() { _con.pop_front(); //禁止vector当适配器,代价太大 //_con.erase(_con.begin()); } T& front() { return _con.front(); } T& back() { return _con.back(); } size_t size() { return _con.size(); } bool empty() { return _con.empty(); } private: Container _con; };

8.仿函数

仿函数就是一个模板参数,根据这个模板参数可以实现控制数据的排序是按升序还是降序。

这个模板参数封装成了类,重载了operator(),通过调用类模板参数的类的运算符重载(operator > 和operator <)来实现自定义类型的比较。

示例:

特殊情况:仿函数需要自己实现

例如:

9.priority_queue模拟实现

template<class T, class Container = vector<T>, class Compare = less<T>> class priority_queue { Compare com; //向下调整 void AdjustDown(int parent) { int child = parent * 2 + 1; while (child < _con.size()) { //if (child + 1 < _con.size() && _con[child] < _con[child + 1]) if (child + 1 < _con.size() && com(_con[child], _con[child + 1])) ++child; if (com(_con[parent], _con[child])) { swap(_con[child], _con[parent]); parent = child; child = parent * 2 + 1; } else break; } } //向上调整 void AdjustUp(int child) { Compare com; int parent = (child - 1) / 2; while (child > 0) { //if (_con[child] >_con[parent]) if (com(_con[parent], _con[child])) { swap(_con[child], _con[parent]); child = parent; parent = (child - 1) / 2; } else break; } } public: priority_queue() { } //迭代器初始化 template<class Inputiterator> priority_queue(Inputiterator first, Inputiterator last) { while (first != last) { _con.push_back(*first); ++first; } //建堆 for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--) { AdjustDown(i); } } void push(const T& x) { _con.push_back(x); //向上调整 AdjustUp(_con.size() - 1); } void pop() { swap(_con[0], _con[_con.size() - 1]); _con.pop_back(); AdjustDown(0); } const T& top() { return _con[0]; } size_t size() { return _con.size(); } bool empty() { return _con.empty(); } private: Container _con; };


觉得不错的,记得点赞+收藏!!!

Read more

【JAVA 进阶】Spring Boot 中 AOP 切面编程全解析:从基础到实战进阶

【JAVA 进阶】Spring Boot 中 AOP 切面编程全解析:从基础到实战进阶

文章目录 * 一、核心概念 * 1.1 什么是面向切面编程(AOP) * 1.2 Spring AOP 核心术语解析 * 1.3 Spring Boot 中启用 AOP 的标准配置 * 二、切点表达式深度解析与实战写法 * 2.1 基础语法与匹配规则 * 2.1.1 execution 表达式核心语法 * 2.1.2 常用通配符详解 * 2.2 基于注解的切点匹配 * 2.2.1 自定义注解驱动切点 * 2.2.2 组合切点提升复用性 * 三、通知类型深度应用与典型场景实现 * 3.1 环绕通知(@Around)

By Ne0inhk

Java 开发 修改【冒险岛 079 私服】完整学习流程

一、前置认知(必看,1 天搞定,建立整体框架) ✅ 1.1 冒险岛 079 私服 整体架构(C/S 架构,Java 核心在服务端)         冒险岛是客户端 (Client) + 服务端 (Server) 架构,我们做的所有修改,95% 的工作都在【Java 服务端】,客户端仅做少量资源修改,这也是 Java 程序员的主战场,完美匹配你的技术栈。 1. 服务端(核心,纯 Java 开发):所有游戏逻辑的核心载体,运行在服务器上,负责处理玩家登录、角色数据、技能释放、怪物 AI、地图加载、道具交易、任务触发、数据库读写等所有核心逻辑,我们的修改

By Ne0inhk
Java 量化系列(十四):输入股票代码,1 秒识别 K 线形态!自动判断涨跌,新手也能精准决策

Java 量化系列(十四):输入股票代码,1 秒识别 K 线形态!自动判断涨跌,新手也能精准决策

炒股最纠结的时刻,莫过于对着一只股票的 K 线图反复琢磨:它现在到底是什么形态?是该买、该卖,还是观望?翻遍教程对比形态,不仅费时间,还容易判断出错…… 这篇系列第十四篇,咱们就把这个痛点彻底解决 —— 打造 “股票 K 线形态一键查询” 功能 !输入股票代码 + 查询日期,系统 1 秒内自动拉取最近 30 天 K 线数据,精准识别 42 种主流形态(头肩顶 / 底、双重顶 / 底、红三兵等),直接给出涨跌概率、趋势分析和操作建议,新手也能像老股民一样精准决策! 一、这个功能有多实用?3 大场景直击炒股刚需 不管是短线交易还是中长期持仓,这个 K 线形态查询功能都能帮你少走弯路: * 选股时:输入候选股票代码,瞬间判断它是否处于 “红三兵”

By Ne0inhk
双非 Java 后端首次实习 | 个人经验分享总结

双非 Java 后端首次实习 | 个人经验分享总结

摘要:实习期间参与企业后台项目开发,熟悉企业开发流程与代码规范。 实习核心流程(结合实际经历) 由于自己进入的是一个小公司实习,当时项目刚好启动,参与了较多基础模块的开发。 一、基础准备与环境搭建阶段(入职 1-3 天) 1. 公司基础配置:进入公司飞书、拥有个人邮箱等基础办公权限 2. 代码拉取与环境搭建: * 学习并使用 git/svn 等版本管理工具 clone 项目代码(公司使用的是阿里云云效) * 配置项目所需配置文件,搭建后端 + 前端开发环境(后端需兼顾前端环境) * 解决环境依赖问题,确保项目能正常跑起来(熟悉配置文件与环境) * 熟悉开发工具的使用,避免因操作问题浪费时间(mentor 教了debug技巧,快捷键) 二、项目熟悉阶段(入职 1-2 周) 这个阶段任务主要是熟悉环境,熟练使用通用封装 / 工具类,自己在熟悉项目的时候,寻找少量项目bug,提交问题给 mentor

By Ne0inhk