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

【C++第十三章】Stack、Queue和Priority_Queue

【C++第13章】Stack、Queue和priority_queue

Stack、Queue和priority_queue介绍🧐

  stack、queue和priority_queue都是容器适配器,它们不用自己管理数据,而是让其他容器管理,自己封装转换

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

  stack和queue不支持迭代器,因为会破坏自身结构。queue也不适配vector,因为结构不匹配。

stack(栈)🧐

  1. stack是一种容器适配器,专门用在LIFO上下文**(后进先出)操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作**。

  2. stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:

    ​   empty:判空操作。

    ​   back:获取尾部元素操作。

    ​   push_back:尾部插入元素操作。

    ​   pop_back:尾部删除元素操作。

  3. 标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器, 默认情况下使用deque。image-20240819165519692

queue(队列)🧐

  1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素

  2. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:

      empty:检测队列是否为空

      size:返回队列中有效元素的个数

      front:返回队头元素的引用

      back:返回队尾元素的引用

      push_back:在队列尾部入队列

      pop_front:在队列头部出队列

  3. 标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque。image-20240819165655945

deque(双端队列)🧐

  stack、queue和priority_queue使用时只需要传类型过去,却不需要传容器,这是因为底层给了容器缺省值——deque(双端队列),deque不是队列,它两端都可以进出,且支持各种功能,与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高

image-20240819155146510

image-20240819155154055

image-20240819164550030

  deque并不是真正的连续空间,而是由一段段连续的小空间拼接出来的,实际deque类似于一个动态的二维数组,然后用一个中控数组(本质是指针数组)控制,当数据满了需要尾插或者头插时,新开一个数组就可以解决,仅有中控数组的扩容消耗,且只需要拷贝指针,代价很低。

Pasted image 20240805130241

  但如果我们要往中间插入数据,就会有两种不同的选择,整体挪动数据、对单个数组进行扩容,前一种挪动效率慢,但是每个数组大小一样,方便访问数据,后一种效率快一点,但是数组大小不一致,不方便访问数据。

image-20240819154938942

  那么deque既然这么好,为什么还会有list和vector容器呢?原因是deque有一个缺陷——不适合遍历,在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,所以在实际使用时,vector和list使用的更多。而deque作为stack和queue的默认容器是因为它俩没有迭代器,且deque扩容效率和内存使用率高,结合了deque的优点而避开了缺点。下面代码是vector和deque的排序速度对比。

```C++
void Test()
{deque<int> de;vector<int> v;for (size_t i = 0; i < 100000; i++){auto j = rand();de.push_back(j);v.push_back(j);}int begin1 = clock();sort(v.begin(), v.end());int end1 = clock();cout << "vector:" << end1 - begin1 << endl;int begin2 = clock();sort(de.begin(), de.end());int end2 = clock();cout << "deque:" << end2 - begin2 << endl;
}

Pasted image 20240805155857

priority_queue(优先级队列)🧐

  priority_queue(优先级队列)也不是传统的队列,它也是容器适配器,可以看做为,并且默认是大堆,排出来是降序,如果需要建小堆,就要用到仿函数

image-20240819160638256

Pasted image 20240809152153

Pasted image 20240809152207

  仿函数实际是一个对象,但他能像函数一样去使用,仿函数大多数地方是为了替代函数指针

Pasted image 20240809160931

  比如优先级队列想变小堆,可以写一份greater类,然后传过去就可以了。

Pasted image 20240809162832

Pasted image 20240809165217

  与sort不同,优先级队列是模板参数,需要传类型过去,sort是函数参数,需要传对象过去,所以使用sort时需要加个扩容,当成匿名对象传过去。

Pasted image 20240809165732

  当我们存入一个对象的指针时可能会出现问题,图中我们写了一个日期的类,并以指针的形式传存储,在多次打印后会发现日期不是最大值。

Pasted image 20240809172951

  原因是这里优先级队列存储的是指针,也就是地址,那么它会按照地址比较,而new出来的地址大小关系是随机的,所以会出现这种情况,解决方法是我们自己写一个仿函数,去修正比较方式。

Pasted image 20240809173402

模拟实现🧐

stack🔎

namespace 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();}const T& top(){return _con.back();}bool empty(){return _con.empty();}size_t size(){return _con.size();}private:Container _con;};
};

queue🔎

namespace 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();}const T& front(){return _con.front();}const T& back(){return _con.back();}bool empty(){return _con.empty();}size_t size(){return _con.size();}private:Container _con;};
}

priority_queue🔎

namespace Priority
{template<class T, class Container = vector<T>,class Compare = Less<T>>class priority_queue{public:priority_queue(){}template<class InputIterator>priority_queue(InputIterator first, InputIterator last):_con(first,last){for (size_t i = (_con.size()-2)/2; i >= 0; i--){AdjustDown(i);}}void AdjustUp(int child) //向上调整{Compare com;int parent = (child - 1) / 2;while (child > 0){//less时等价于if(_con[parent] < _con[child])if(com(_con[parent],_con[child])){swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else{break;}}}void AdjustDown(int parent) //向下调整{Compare com;int child = parent * 2 + 1;while (child < (int)_con.size()){if (child + 1 < (int)_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 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];}bool empty(){return _con.empty();}private:Container _con;};
}

结尾👍

  以上便是Stack、Queue和Priority_Queue的全部介绍,如果有疑问或者建议都可以私信笔者交流,大家互相学习,互相进步!🌹

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Scikit-learn:用于数据挖掘和数据分析的简单而有效的工具,建立在 NumPy, SciPy 和 Matplotlib 上。
  • 【数学分析笔记】第2章第2节数列极限(2)
  • 《深入浅出算法竞赛》-递推与递归(笔记版)
  • Python之函数的使用
  • ChatGLM-6B 主要代码分析 RotaryEmbedding
  • vulnhub靶机 DC-9(渗透测试详解)
  • 顺丰科技25届秋季校园招聘常见问题答疑及校招网申测评笔试题型分析SHL题库Verify测评
  • IO器件性能评估
  • 刷刷前端手写题
  • 理解JavaScript的基本概念和语法:让网页动起来
  • 【笔记】Android 多用户模式和用户类型
  • Codeforces Round 965 (Div. 2)
  • 如何对 GitLab 中文版进行升级?
  • 鸿蒙内核源码分析(进程管理篇) | 谁在管理内核资源?
  • cpu管理
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • 5、React组件事件详解
  • CSS实用技巧干货
  • ECMAScript6(0):ES6简明参考手册
  • extract-text-webpack-plugin用法
  • HTTP中GET与POST的区别 99%的错误认识
  • JAVA之继承和多态
  • Laravel 实践之路: 数据库迁移与数据填充
  • Ruby 2.x 源代码分析:扩展 概述
  • SQL 难点解决:记录的引用
  • SQLServer之创建显式事务
  • yii2权限控制rbac之rule详细讲解
  • 对象引论
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 基于webpack 的 vue 多页架构
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 如何邀请好友注册您的网站(模拟百度网盘)
  • 使用Gradle第一次构建Java程序
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 用 Swift 编写面向协议的视图
  • Unity3D - 异步加载游戏场景与异步加载游戏资源进度条 ...
  • 容器镜像
  • 小白应该如何快速入门阿里云服务器,新手使用ECS的方法 ...
  • ​RecSys 2022 | 面向人岗匹配的双向选择偏好建模
  • # Swust 12th acm 邀请赛# [ E ] 01 String [题解]
  • # 飞书APP集成平台-数字化落地
  • ######## golang各章节终篇索引 ########
  • #pragma once
  • $refs 、$nextTic、动态组件、name的使用
  • (CVPRW,2024)可学习的提示:遥感领域小样本语义分割
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (南京观海微电子)——示波器使用介绍
  • (十六)串口UART
  • (十一)c52学习之旅-动态数码管
  • (四)activit5.23.0修复跟踪高亮显示BUG
  • (一)Kafka 安全之使用 SASL 进行身份验证 —— JAAS 配置、SASL 配置
  • (最优化理论与方法)第二章最优化所需基础知识-第三节:重要凸集举例