【C++】——Stack与Queue(含优先队列(详细解读)

前言

之前数据结构中是栈和队列,我们分别用的顺序表和链表去实现的,但是对于这里的栈和队列来说,他们是一种容器,更准确来说是一种容器适配器

✨什么是容器适配器?

 从他们的模板参数可以看出,第二个参数模板是一个容器,并不像vector和list一样是一个内存池

至于deque后面会介绍

这里写传容器的原因就是,栈和队列是直接复用了其他的容器,用他们的功能实现自己的功能,就比如每家的电压和自己手机充电器的电压是不一样的,但是我用一个适配器就可以转换出来

 

一  Stakc的介绍和使用

1.1 Stakc介绍

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

Stack是没有迭代器的,因为栈不能说是去遍历每个元素,而是后进先出的结构,所以设计迭代器没有任何意义,虽然可以去设计

 

1.2  栈的使用 

 对于之前数据结构来说,我们需要自己写一个栈,这里就直接引入头文件就行。

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<list>
#include<stack>
using namespace std;
int main()
{stack<int> st;st.push(1);st.push(2);st.push(3);st.push(4);st.push(5);int n = st.size();while (n--){cout << st.top() << " ";st.pop();}cout << endl;
}

 

这里只是一个简单的演示

下面来做一个题目:

155. 最小栈

提示

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

  • MinStack() 初始化堆栈对象。
  • void push(int val) 将元素val推入堆栈。
  • void pop() 删除堆栈顶部的元素。
  • int top() 获取堆栈顶部的元素。
  • int getMin() 获取堆栈中的最小元素。

示例 1:

输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]输出:
[null,null,null,null,-3,null,0,-2]解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.

提示:

  • -231 <= val <= 231 - 1
  • poptop 和 getMin 操作总是在 非空栈 上调用
  • pushpoptop, and getMin最多被调用 3 * 104 次

 思路:

对于这道题目来说,要拿出最小的那个元素,那么我们必须去记录,如果我们用一个变量去保存这个最小值肯定是不行的,因为我们用这个变量保存一个最小值以后,然后我们再删这个最小值,那当前最小就不知道是谁了,得重新来一遍,这样虽然可行,但是在时间效率上可能会很低

所以解决这道题可以用两个栈去模拟,一个栈存最小值,一个栈做为入栈,只要比最小栈的最小元素还小的时候(就是栈顶元素)就可以加入到这个最小栈里面,我们删除数据的时候,如果删除的数据和栈顶元素相等,那么我们把最小栈的元素删除,否则不动。

有了思路,现在来写代码就简单很多了

class MinStack {
public:MinStack() {}void push(int val) {_elem.push(val);//先入if(_min.empty()||val<=_min.top())//如果最小栈是空或者当前值比最小栈的栈顶元素还小就加入{_min.push(val);}}void pop() {if(_elem.top()==_min.top())//相等就一起删,最后最小栈栈顶元素就是最小的{_min.pop();}_elem.pop();}int top() {return _elem.top();//其他函数按规则来就行}int getMin() {return _min.top();}private:std::stack<int> _elem;//设置两个栈std::stack<int> _min;};/*** Your MinStack object will be instantiated and called as such:* MinStack* obj = new MinStack();* obj->push(val);* obj->pop();* int param_3 = obj->top();* int param_4 = obj->getMin();*/

  二  Queue的介绍和使用

2.1 队列的介绍

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

 

2.2  队列的使用 

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<list>
#include<stack>
#include<queue>
using namespace std;
int main()
{queue<int>q;q.push(1);q.push(2);q.push(3);q.push(4);int n = q.size();while (n--){cout << q.front() << " ";q.pop();}cout << endl;return 0;
}

三  优先级队列的介绍和使用

3.1 优先队列介绍

   1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
    2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元 素)。
    3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
    4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
    empty():检测容器是否为空
    size():返回容器中有效元素个数
    front():返回容器中第一个元素的引用
    push_back():在容器尾部插入元素 

    pop_back():删除容器尾部元素
   5. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定priority_queue类实例化指定容器类,则使用vector。
6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。

优先队列有三个模板参数,一个是 数据类型,一个是容器,一个是比较模板

到这里我们可以得知优先队列默认就是一个大堆

其中比较这里的比较采用的是模板,并不是之前的函数,但是这里可以用比较函数去进行,也就是用一个函数指针,但是c++不喜欢这种做法,并引伸出了仿函数

✨仿函数

仿函数就是把一个写成函数的形式

template<class T>
class less
{
public:bool operator(const T&x,const T& y){return x < y;}
};

这里是一个比较大小的仿函数,同时也是一个模板类型, 如果确定类型也可以不用写成模板类型

我们可以直接用这个作为比较函数,完成比较。

3.2 优先队列的使用

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include <functional>
using namespace std;
// greater算法的头文件
int main()
{// 默认情况下,创建的是大堆,其底层按照小于号比较vector<int> v{ 3,2,7,6,0,4,1,9,8,5 };priority_queue<int> q1;for (auto& e : v)  q1.push(e);cout << q1.top() << endl;// 如果要创建小堆,将第三个模板参数换成greater比较方式priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());cout << q2.top() << endl;return 0;
}

优先队列优先是大堆,这里第三个模板参数的改变也就意味比较函数的改变

四 三种容器的模拟实现

4.1 stack模拟实现

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

4.2  queue模拟实现

 

#pragma once
template<class T,class Container=deque<T>>
class Queue
{
public:void push(const T&x){_con.push_back(x);}void pop(){_con.pop_front();}size_t size(){return _con.size();}bool empty(){return _con.empty();}T& front(){return _con.front();}const T& front()const {return _con.front();}T& back(){return _con.back();}const T& back()const{return _con.back();}
private:Container _con;
};

4.3  优先队列的模拟实现

#pragma once
template<class T,class Container=vector<T>>
class Priority_queue
{
public:Priority_queue()//无参构造{}//迭代器构造template<class InputIterator>Priority_queue(InputIterator first, InputIterator last):_con(first,last){for (int i = (_con.size() - 1) / 2; i >= 0; i--){adjust_down(i);}}//向上调整void adjust_up(int child){int father = (child-1)/2;while (child > 0){if (_con[father] < _con[child]){swap(_con[child], _con[father]);child = father;father = (child - 1) / 2;}else{break;}}}//向下调整void adjust_down(int father){int child = father * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && _con[child] < _con[child + 1]){child++;}if (_con[father] < _con[child]){swap(_con[father], _con[child]);father = child;child = father * 2 + 1;}else{break;}}}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(){return _con.empty();}size_t size(){return _con.size();}
private:Container _con;//容器对象
};

测试

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include <functional>
using namespace std;
#include"priority_queue.h"
int main()
{vector<int>v = { 3,4,5,1,8,7,8 };Priority_queue<int>p(v.begin(), v.end());int n = p.size();while (n--){cout << p.top() << " ";p.pop();}cout << endl;return 0;
}

 

 

上面的模拟构造并没有使用第三个参数,也就是没有仿函数,优先队列其实是复用了vector容器,看似是堆,但内部结构是一个可变的数组,其中的每个操作步骤也就是在操作数组里面的元素

还有一个加入仿函数的例子

#pragma once
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 (int i = (_con.size() - 1) / 2; i >= 0; i--){adjust_down(i);}}//向上调整void adjust_up(int child){int father = (child-1)/2;while (child > 0){if (_com(_con[father] , _con[child])){swap(_con[child], _con[father]);child = father;father = (child - 1) / 2;}else{break;}}}//向下调整void adjust_down(int father){int child = father * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && _com(_con[child] , _con[child + 1])){child++;}if (_com(_con[father] , _con[child])){swap(_con[father], _con[child]);father = child;child = father * 2 + 1;}else{break;}}}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(){return _con.empty();}size_t size(){return _con.size();}
private:Container _con;//容器对象Compare _com;//仿函数对象
};

 注意模板参数的变化和比较大小代码的变化,仿函数是写在外面的,具体怎么比,按自己的需求来

测试:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include <functional>
using namespace std;
#include"priority_queue.h"template<class T>
class Less
{
public:bool operator()(const T& x,const T& y){return x < y;}
};
template<class T>
class Greater
{
public:bool operator()(const T& x, const T& y){return x >  y;}
};
int main()
{vector<int>v = { 3,4,5,1,8,7,8 };Priority_queue<int,vector<int>,Greater<int>>  p(v.begin(), v.end());int n = p.size();while (n--){cout << p.top() << " ";p.pop();}cout << endl;return 0;
}

 

 五  deque

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

 和vector和list比起来,它比vector头插的效率高,和list比,它支持随机访问,且空间利用率比list高,但是它都无法替代他们两个,反倒成了stack和queue的底层容器

对于deque来说,它是一个指针数组,这些指针指向一片片空间,它并不真正的连续,而是由这些空间拼接而成

 从上面我们可知道的是,如果我们在中间插入数据,那么就会面临两种选择

1.扩容,这样减少了挪动数据,但是使得每个数组的空间大小不一样,这里数组空间的大小不一样就会导致我们在查找数据的时候需要一个个访问,这样就导致效率变得很低

2.挪动数据,也就是保持数组长度的不变,但是这样挪动的数据就太多了,也会导致效率变低,但是好处就是我们在访问元素的时候,效率会很高,假设每个数组都是10,我们要访问第i个元素,那么我先用i-=第一层的元素个数,然后x=i/10,这里的x就是第几层(层数从0开始),然后y=i%10这里的y是第几个,这样就可以快速的定位了

因为deque有这些缺点,所以也不能替代vector和list

总结

以上就是全部内容了,希望喜欢,多多支持

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

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

相关文章

Kafka 架构

1 整体架构 1.1 Zookeeper Zookeeper 是一个分布式协调服务&#xff0c;用于管理 Kafka 的元数据。它负责维护 Kafka 集群的配置信息、Broker 列表和分区的 Leader 信息。 Zookeeper 确保了 Kafka 集群的高可用性和可靠性。 但 Zookeeper 已经成为 Kafka 性能瓶颈&#xff0c;…

SmartDraw Suite Edition 画图软件画表格内存示意图的使用方法

总述&#xff1a;遇到不会画的&#xff0c;比如如何画一条虚线&#xff0c;先将 虚线 翻译成英文&#xff0c;然后在 help 中查询。 新建的时候选择如下&#xff1a; 一、选择 Forms->Blank Form 二、画表格&#xff1a; 三、画箭头&#xff1a;先选择1在选择2 四、编辑文…

ASUS华硕ROG幻14Air笔记本GA403UI(UI UV UU UJ)工厂模式原厂Windows11系统安装包,带MyASUS in WinRE重置还原

适用型号&#xff1a;GA403UI、GA403UV、GA403UU、GA403UJ 链接&#xff1a;https://pan.baidu.com/s/1tz8PZbYKakfvUoXafQPLIg?pwd1mtc 提取码&#xff1a;1mtc 华硕原装WIN11系统工厂包带有ASUS RECOVERY恢复功能、自带面部识别,声卡,显卡,网卡,蓝牙等所有驱动、出厂主题…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 6月9日,星期日

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年6月9日 星期日 农历五月初四 1、 人社部&#xff1a;个人养老金开户人数已超6000万&#xff0c;其中31岁至40岁的中高收入人群是开户、缴费和购买产品的主力军。 2、 医保局刊文&#xff1a;研究显示集采仿制药替代原研药…

VSC++: 民意调查比例法

void 民意调查比例法() {//缘由https://bbs.csdn.net/topics/396521294?page1#post-411408461从题目描述看&#xff1a;902/3~300.7&#xff0c;1498/5~299.6也就是大约求2个数的公约数&#xff0c;并使得这个公约数尽量求出最小误差&#xff1f;且商小于某值。int a 0, aa …

物理安全防护如何创新强化信息安全体系?

物理安全防护是信息安全体系的重要组成部分&#xff0c;它通过保护实体设施、设备和介质等&#xff0c;防止未授权访问、破坏、盗窃等行为&#xff0c;从而为信息系统提供基础的安全保障。要创新强化信息安全体系中的物理安全防护&#xff0c;可以从以下几个方面着手&#xff1…

VBA高级应用30例应用2实现在列表框内及列表框间实现数据拖动

《VBA高级应用30例》&#xff08;版权10178985&#xff09;&#xff0c;是我推出的第十套教程&#xff0c;教程是专门针对高级学员在学习VBA过程中提高路途上的案例展开&#xff0c;这套教程案例与理论结合&#xff0c;紧贴“实战”&#xff0c;并做“战术总结”&#xff0c;以…

Facebook:社交世界的引领者

导语 在当今数字化时代&#xff0c;Facebook已经成为了人们社交生活的重要一环。然而&#xff0c;除了成为社交媒体的象征外&#xff0c;它还在不断探索并领导着社交世界的新方向。 1. 社交平台的发展者 Facebook不仅仅是一个社交平台&#xff0c;更是社交方式的引领者。从其…

MathType7.8永久破解版下载 让数学学习变得简单有趣!

大家好&#xff0c;我是科技评论家。今天给大家推荐一款非常实用的数学公式编辑器——MathType 7.8&#xff01;&#x1f4f1;&#x1f4b0; 在数字化时代&#xff0c;学术研究、教学和科研领域中的数学公式编辑需求越来越高。而MathType 7.8作为一个广受欢迎的数学公式编辑器&…

swaks一键启动邮箱信息测试(KALI工具系列十九)

目录 1、KALI LINUX 简介 2、swak工具简介 3、临时邮箱 4、操作实例 4.1 测试邮箱连通性 4.2 假冒发送方 4.3 发送文件 5、总结 1、KALI LINUX 简介 Kali Linux 是一个功能强大、多才多艺的 Linux 发行版&#xff0c;广泛用于网络安全社区。它具有全面的预安装工具和功…

MSP432E401Y Launchpad硬件电路

MSP432E401Y是一款32位Arm Cortex-M4F内核的MCU&#xff0c;主频120MHz、256KB SRAM、1MB Flash、6KB EEPROM&#xff0c;具有丰富的通信外设&#xff0c;例如支持以太网、2个CAN、8个UART、4个QSSI(SPI)、10 个I2C; 同时还有2个12 位SAR的ADC模块&#xff0c;每个模块支持高…

探索智慧景区的总体架构与应用

背景&#xff1a; 在旅游业快速发展的今天&#xff0c;智慧景区已成为提升景区管理水平、提高游客体验的重要手段之一。智慧景区系统的总体架构设计与应用&#xff0c;将现代信息技术与景区管理相结合&#xff0c;为景区的运营管理和游客服务提供了新的思路和解决方案。本文将…

【诈骗离你我很近】中国同胞进来看看国外诈骗新套路。

前几天一个老外经常在CSDN给我发消息&#xff0c;我最开始很警惕&#xff0c;不过聊了大概半个月&#xff0c;我就没怎么怀疑他了&#xff0c;而且还很高兴认识了一个外国朋友。这半个月聊天内容很正常&#xff0c;就聊些中国的小习惯&#xff0c;让我教他用筷子。还问我有哪些…

【BUG】已解决: No module named ‘torch._six

已解决&#xff1a;No module named ‘torch._six 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;目前是武汉城市开发者社区主…

GPT-4欺骗人类的惊人成功率达99.16%!

PNAS重磅研究揭示&#xff0c;LLM推理能力越强欺骗率越高&#xff01;&#xff01; 此前&#xff0c;MIT的研究发现&#xff0c;AI在各类游戏中为了达到目的&#xff0c;不择手段&#xff0c;学会用佯装和歪曲偏好等方式欺骗人类。 GPT-4o深夜发布&#xff01;Plus免费可用&…

数据加密验签机的工作原理

数据加密验签机&#xff0c;作为网络安全领域的关键设备&#xff0c;其重要性不言而喻。以下是对数据加密验签机的详细介绍&#xff1a; 一、引言 在数字化时代&#xff0c;数据的机密性、完整性和真实性是企业和个人都极为关注的问题。数据加密验签机&#xff0c;正是为了解决…

【学术小白成长之路】02三方演化博弈(基于复制动态方程)期望与复制动态方程

从本专栏开始&#xff0c;笔者正式研究演化博弈分析&#xff0c;其中涉及到双方演化博弈分析&#xff0c;三方演化博弈分析&#xff0c;复杂网络博弈分析等等。 先阅读了大量相关的博弈分析的文献&#xff0c;总结了现有的研究常用的研究流程&#xff0c;针对每个流程进行拆解。…

Dubbo 3.x源码(21)—Dubbo服务引用源码(4)

基于Dubbo 3.1&#xff0c;详细介绍了Dubbo服务的发布与引用的源码。 此前我们学习了createInvokerForRemote方法中的Wrapper有哪些以及作用&#xff0c;接下来我们将会的学习真正的本地、应用级别、接口级别的Protocol的引入逻辑&#xff0c;以及创建Proxy服务接口代理对象的逻…

分享不用会员免费听歌的软件,可听付费,支持随听随下!

今天来点特别的&#xff0c;给你们带来几款全网免费听歌的神器&#xff0c;让你们的音乐之旅不再有障碍&#xff01; 现在&#xff0c;找好听的歌越来越像寻宝一样&#xff0c;动不动就得掏腰包。不过别担心&#xff0c;阿星今天就来分享几款好用的免费听歌app&#xff0c;电脑…

六、【源码】SQL执行器的定义和实现

源码地址&#xff1a;https://github.com/mybatis/mybatis-3/ 仓库地址&#xff1a;https://gitcode.net/qq_42665745/mybatis/-/tree/06-sql-executor SQL执行器的定义和实现 之前的Sql执行都是耦合在SqlSession里的&#xff0c;现在要对这部分进行解耦和重构&#xff0c;引…