C++STL之stack,queue与容器适配器 - 教程

news/2025/10/14 19:03:30/文章来源:https://www.cnblogs.com/slgkaifa/p/19141798

C++STL之stack,queue与容器适配器 - 教程

目录

  • C++STL之栈与队列,容器适配器
      • 容器适配器介绍
      • 栈(Stack)与队列(Queue)
        • 一、 栈(Stack):后进先出
        • 二、 队列(Queue):先进先出
        • 三、 优先级队列(Priority Queue):优先级最高的元素先出
        • 四、双端队列(Deque):队头队尾可进可出

C++STL之栈与队列,容器适配器

容器适配器介绍

什么是容器适配器?
适配器是一种设计模式,它就像一个转换头,能将一个类的接口转换成用户所希望的另一种接口。STL中的stack、queue和priority_queue都是容器适配器,它们没有自己独立的数据结构,而是“适配”了其他容器(如deque, vector)的接口,提供了新的、特定的行为。

优点:

容器适配器通过封装和限制接口,将通用的顺序容器转化为具有特定语义的专用数据结构。

首先在代码清晰度方面,使用 stack 比直接用 vector 并约定只能操作尾部更能明确表达程序员的意图;并且容器适配器防止了误操作,因为开发者无法随意访问(不提供相应接口,通过封装后只暴露需要的功能接口)栈中间或队列中间的元素,强制遵守特定数据结构的访问规则;同时这种设计还具有很好的复用性,无需重新实现栈、队列等基础数据结构,而是直接复用现有容器的功能,给用户带来更加便捷的编程方式。

如何实现

我们在自定义类的时候可以定义为模板类,这就需要我们在实例化的时候传递相应的类型,同理,我们可以传入容器的类型,应该类型去定义成员属性,同时对该成员属性的方法进行封装,实现我们所需要的方法。

如下所示:

template <class T, class container>class stack{public://…… 封装相关方法private:container _st; //该容器被适配成stack}int main(){stack<int, vector<int>> vst; //底层使用vecotr,调用vecotr方法实现的栈stack<int, list<int>> lst; //底层使用list,调用list方法实现的栈return 0;}

栈(Stack)与队列(Queue)

一、 栈(Stack):后进先出

栈是一种严格遵守后进先出(LIFO) 原则的线性数据结构。你可以把它想象成一个只有一个口的圆筒,我们只能从这个口放入或取出物品,最后放进去的,总是最先被拿出来。

栈的实现与使用:
栈在C++标准库中并未作为一个独立的容器实现,而是作为一种**“容器适配器”**。这意味着它是在其他基础容器(如vector、deque、list)之上,通过封装其特定接口(只提供push_back, pop_back, top等操作)而构建的。默认情况下,stack使用deque作为其底层容器。

它的接口非常直观:

无迭代器的原因:

栈只能后进先出,因此没有遍历或者访问处于中间位置的元素的诉求,因此没有迭代器

模拟实现:
由于其行为与只在一端操作的线性表(vector,list,dqueue)高度相似,我们可以轻松地用已经存在的线性表来适配出一个栈,将所有插入和删除操作都限制在线性表的尾部即可。

构造函数和析构函数与可以使用编译器默认生成的,因为只有一个成员变量(Container _st),而且是自定义类型,会去调用他自己的构造函数和析构函数,

#pragma once
#include<iostream>#include<vector>#include<list>namespace mystack{template<class T, class Container = std::vector<T>> //默认使用vecotr<T>, 然而在C++STL标准中默认使用的是dqueueclass stack{private:Container _st;public:void push(const T& val){_st.push_back(val);}void pop(){_st.pop_back();}const T& top(){return _st.back();}bool empty(){return _st.empty();}size_t size(){return _st.size();}void swap(stack& val){_st.swap(val._st);}};}int main(){mystack::stack<int> st1; //默认使用vecotr<int> 作为底层容器for (int i = 0; i < 5; i++){st1.push(i);}mystack::stack<int> st2;for (int i = 5; i < 9; i++){st2.push(i);}st1.swap(st2);while (!st1.empty()){std::cout << st1.top() << ' ';st1.pop();}std::cout << std::endl;while (!st2.empty()){std::cout << st2.top() << ' ';st2.pop();}mystack::stack<int, std::list<int>> st3;  //也可以使用list作为底层容器 mystack::stack<int, std::dqueue<int>> st4;  //dqueue作为底层容器 //……return 0;}

二、 队列(Queue):先进先出

队列则遵循先进先出(FIFO) 的原则,就像现实生活中的排队。最早进入队伍的人,将最早被服务。

队列的实现与使用:
和栈一样,queue也是一种容器适配器。它要求底层容器必须支持从后端插入(push_back)和从前端删除(pop_front)。vector,list和deque都满足这个要求,默认情况下,它使用deque作为底层容器。

其核心接口包括:

  • push(val):元素val进入队尾。
  • pop():队头元素出队。
  • front()和back():分别获取队头和队尾元素的引用。
  • empty()和size():分别用于判断队列是否为空和获取元素个数。
  • swap(que):与que队列进行交换

无迭代器的原因:

与栈相同,队列也没有遍历或者访问处于中间位置的元素的诉求,因此没有迭代器

模拟实现:

与栈的模拟实现相同,选择一个使用适配器模式可以很轻松的实现一个队列

由于queue需要在两端操作,而vector的头删效率很低,因此在模拟实现时,通常选择list作为底层容器,可以高效地进行头删和尾插。

#pragma once
#include<iostream>#include<vector>#include<list>namespace myqueue{template<class T, class Container = std::list<T>>class queue{private:Container _que;public:void push(const T& val){_que.push_back(val);}void pop(){_que.pop_front();}const T& back(){return _que.back();}const T& front(){return _que.front();}bool empty(){return _que.empty();}size_t size(){return _que.size();}void swap(queue& val){_que.swap(val._que);}};}int main(){myqueue::queue<int> que1;  //默认list<int> 为底层容器myqueue::queue<int> que2;for (int i = 0; i < 5; i++){que1.push(i);}for (int i = 5; i < 9; i++){que2.push(i);}//que1.swap(que2);while (!que1.empty()){std::cout << que1.front() << ' ';que1.pop();}std::cout << std::endl;while (!que2.empty()){std::cout << que2.front() << ' ';que2.pop();}std::cout << std::endl;myqueue::queue<int, std::vector<int>> que3;  //也可以使用vector作为底层容器,但是效率低,极其不推荐 myqueue::queue<int, std::dqueue<int>> que3;  //dqueue作为底层容器 //……return 0;}

三、 优先级队列(Priority Queue):优先级最高的元素先出

优先级队列不再是简单的先进先出或后进先出,它其中的元素都带有“优先级”。出队时,永远是当前队列中优先级最高(或最低)的那个元素先出去。它的底层本质就是一个

优先级队列的实现与使用:
priority_queue也是一个容器适配器,默认使用vector作为底层容器,并在其上应用堆算法(如make_heap, push_heap, pop_heap)来维护堆结构。默认情况下,它是一个大堆,即堆顶元素最大。

我们可以通过模板参数来控制它是大堆还是小堆:

  • priority_queue< int >:默认,大堆。
  • priority_queue<int, vector< int >, greater< int >>:小堆。

其核心接口包括:

  • push(val):元素val插入队列,其插入位置由内部的堆维护。
  • pop():删除堆顶元素(最大的或者最小的)。
  • top():获取堆顶元素
  • empty()和size():分别用于判断队列是否为空和获取元素个数。
  • swap(pque):与pque优先级队列进行交换

自定义类型:
如果我们要在优先级队列中存放自定义类型(比如一个 less 类),就必须在该类中重载 <> 运算符,因为优先级队列需要知道如何比较这些对象的优先级。

我们可以重载( )来作为比较的运算符,完全模拟库中的less和greater

template <class T>struct less{bool operator()(const T& x, const T& y){return x < y;}};template <class T>struct greater{bool operator()(const T& x, const T& y){return x > y;}};int main(){less<int> cmp; //生成该类的对象cmp(1, 2);  //运算符重载,返回 1 < 2 的值cmp(3, 1);return 0;}

有了比较的方法,我们就可以控制最大堆和最小堆。

在实现的时候,核心在与如何维护任一节点都大于其后代节点,我们可以通过向上调整和向下调整来维护

当插入一个值的时候,我们将其放在最后一个叶子节点,然后通过向上调整来使堆合法化

当删除堆顶元素的时候,我们将堆顶元素与最后一个叶子节点交换,然后向下调整来使堆合法化

模拟实现代码:

#pragma once
#include<iostream>#include<vector>#include<list>namespace mypriority_queue{template <class T>struct less{bool operator()(const T& x, const T& y){return x < y;}};template <class T>struct greater{bool operator()(const T& x, const T& y){return x > y;}};template<class T, class Container = std::vector<T>, class Compare = less<T> >class priority_queue{private:Container _pque;public:// 0//1 2//3 4 5 6void AdjustUp(){int child = _pque.size() - 1;int parent = (child - 1) / 2;Compare com;while(child > 0){if(com(_pque[parent], _pque[child])){std::swap(_pque[parent], _pque[child]);}else{break;}child = parent;parent = (child - 1) /2;}}void AdjustDown(){int parent = 0;int child = parent * 2 + 1;Compare com;while(child < _pque.size()){if(child + 1 < _pque.size() && com(_pque[child] , _pque[child + 1])){child++;}if(com(_pque[parent], _pque[child])){std::swap(_pque[parent], _pque[child]);}else{break;}parent = child;child = parent * 2 + 1;}}void push(const T& val){_pque.push_back(val);AdjustUp();}void pop(){std::swap(_pque[0], _pque[_pque.size()-1]);_pque.pop_back();AdjustDown();}const T& top(){return _pque.front();}bool empty(){return _pque.empty();}size_t size(){return _pque.size();}void swap(priority_queue& val){_pque.swap(val._pque);}};}int main(){mypriority_queue::priority_queue<int, std::vector<int>, mypriority_queue::greater<int>> qu ;qu.push(4);qu.push(9);qu.push(7);qu.push(2);qu.push(5);qu.push(4);qu.push(9);qu.push(7);while (!qu.empty()){std::cout << qu.top() << ' ';qu.pop();}return 0;}

四、双端队列(Deque):队头队尾可进可出

deque(双端队列)是一个两端都能高效插入删除的数据结构,但它并非真正连续,而是由多个分段连续的空间组成。他结合了vector和list的优点,中和他两个的效率,但同时也带来了弊端

  • 优点:头尾插删效率都是O(1),且扩容时代价比 vector 小。
  • 缺点:遍历效率较低,因为迭代器需要频繁检查是否跨越了段边界。

具体实现:

deque的底层存在一个中控数组(指针数组),其内部存放的是指向一个个内存块的指针,每个内存块中可以存放一定量的数据。

这个中控数组从中间开始存放数据,也就是说,中控数组的头部一开始是空的,这样再进行头插的时候就无需挪动数据,如果中控数组中第一个有效的数据块存放满了,就重新开辟一块空间,然后在这块空间的尾部存放数据,如果没有满,那就在第一块有效数据块从后往前找没有存放数据的空间存放数据。

在这里插入图片描述

若是中控数组也满了,那么进行扩容,只需扩大中控数组,复制其内部的指针,无需复制内存块内容,因此其扩容效率大大提升

deque的接口与vector几乎相同,这里不过多介绍。

STL选择 deque 作为 stack 和 queue 的默认底层容器,是一个精妙的权衡:

  1. 无需遍历:stack 和 queue 不支持遍历,完美避开了 deque 遍历效率低的缺陷。
  2. 高效扩容:对于stack,deque 在元素增长时比 vector 效率更高(无需大量搬移数据)。对于queue,deque 同时具备了高效率和较高的内存利用率。

总结来说,deque 在 stack 和 queue 的应用场景中,恰到好处地结合了 vector 和 list 的优点,同时规避了自身的缺陷

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

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

相关文章

2025年氧化镁厂家最新推荐排行榜,电工级/高温/低温/中温/防火电缆/矿物绝缘/熔盐加热器/电热管用/单头管用/合成云母用氧化镁公司推荐!

随着工业技术的不断进步,氧化镁在各个领域的应用越来越广泛。从电工级氧化镁到高温、低温、中温氧化镁,再到防火电缆氧化镁、矿物绝缘氧化镁、熔盐加热器氧化镁、电热管用氧化镁、单头管用氧化镁以及合成云母用氧化镁…

智能体分析

AI智能体技术正经历快速发展,从早期的简单对话机器人演进为能够自主规划、执行复杂任务的多模态系统。本次将对市面上功能较为完善的智能体平台进行系统分析,从通用型、垂直领域、开源/免费工具三个维度,对比它们的…

Excel - lookup()

Excel - lookup() Excel lookup(B3, sheet2!A3:A33, sheet2!E3:E33), 向下填充时如何只增加第1个参数值?例如增加B3为B4, B5...,保持sheet2!A3:A33, sheet2!E3:E33不变。 ChatGPT said:你这个问题是 Excel 相对/绝对…

2025 年玄武岩厂家推荐榜:玄武岩/0-3mm/3-5mm/5-10mm/10-15mm/10-20mm/石子厂,聚焦基建升级与高端化需求,山东展飞建筑材料有限公司成优选

随着国内基建工程持续推进(高铁、高速、市政项目等)、绿色建材标准趋严及高端新材料需求逐步释放,玄武岩建筑材料凭借高强度、耐磨损等特性,已从传统基建专属逐步拓展至道路铺建、景观绿化、污水治理等多个领域,2…

2025 佛山铝合金/系统/断桥铝/耐用/推拉/封阳台/别墅/静音门窗厂家品牌实力推荐:聚焦技术与服务的五大优选标杆

在家居装修与建筑工程中,门窗的性能与品质直接影响居住舒适度与建筑安全性。随着行业向 "科技美学" 升级,兼具技术创新、品质保障与服务能力的品牌愈发受到关注。以下结合市场调研与实际应用场景,推荐五个…

Ubuntu22.04 server网络配置

配置ubuntu ip地址新安装的 Ubuntu 服务器首需要配置 ip 才能进行网络连接。 1 配置 IP 1.1 查看网卡信息 sudo apt install lshw -y sudo lshw -C network例如我这里使用有线网网卡名是 enp3s01.2 配置 ip 编辑 netpl…

完整教程:深度学习优化器全面指南:核心参数选择与实战策略

完整教程:深度学习优化器全面指南:核心参数选择与实战策略pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Conso…

C#——方法的定义、调用与调试 - 详解

C#——方法的定义、调用与调试 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mona…

说说新版畅联云的一些重要约定

先了解几个基本概念:品目类似文件夹,而且从顶级来看,畅联将AIoT分成了3大分类,一个是视频设备,一个是IoT设备,一个是算法。品目是无限级的,可以一层层建一下。产品产品属于某一个具体品目,譬如某个4G安全帽产品…

App.vue(完整可运行示例)

🔧 TXT 文件 URL 批量替换工具将文件中的 https://alicdn-imags.zsgw.vip/default/20250826/imags 替换为 https://site.shrtxs.cn/agent <div><inputtype="file"ref="fileInput"@chan…

Windows MySQL 报错

1. 首次更改密码报错解决 ERROR 1820 (HY000) ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement. 问题解决修改密码 ALTER USER username@host IDENTIF…

Redis:高性能内存数据库的六大核心优势 - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

lvgl刷新回调事件实现说明

static void my_lvgl_flush_cb(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p) {lock(1); #if 1uint32_t offset = 0;int32_t x, y;for (y = area->y1; y <= area->y2; y++) {for (x…

Avalonia Behaviors 在 StackPanel 空白处无效问题解析与解决方案

问题描述 在 Avalonia UI 开发中,很多开发者会遇到这样的问题:在 StackPanel 上添加了 Behaviors 和事件触发器,但是只有在 StackPanel 内部的文本、按钮等可视化元素上点击才有效,而在 StackPanel 的空白区域点击…

完整教程:Django 入门:快速构建 Python Web 应用的强大框架

完整教程:Django 入门:快速构建 Python Web 应用的强大框架pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Cons…

Hyperliquid 的稳定币USDH发行机制与发行商竞选指南

引言:稳定币是什么,为什么 Hyperliquid 需要它? 想象一下,在加密货币世界里,价格像过山车一样波动,你需要一个“稳定锚”来固定价值——这就是稳定币(Stablecoin)。稳定币是一种设计成与美元或其他资产挂钩的加…

windows上建简单的ssh版git仓库

说来话长,公司用的svn,内网,我想弄个简单的git仓库客户端自己用,问了好久的AI,真垃圾的这个AI。 过程如下: 1、Windows自带的OpenSSH 自己找找网上怎么开启,没有的话离线下载: 离线下载 2、安装git,这里就不说…

2025年聚合硫酸铁供应厂家如何选?行业权威指南与成本控制策略?

文章摘要 本文深入分析2025年聚合硫酸铁供应市场趋势,从生产技术、质量把控、成本优化等多维度解析如何选择靠谱供应商。以铜陵君尔源环保科技为例,通过实际案例揭示优质厂家的核心优势,为企事业单位提供科学的采购…

高级语言程序第一次作业

这个作业属于哪个课程:https://edu.cnblogs.com/campus/fzu/gjyycx 这个作业要求在哪里:https://edu.cnblogs.com/campus/fzu/gjyycx/homework/13558 学号:102500332 姓名:郑雷 1.成功安装编译环境结果如图 捕获 2…