C++第八讲:STL--stack和queue的使用及模拟实现

C++第八讲:STL--stack和queue的使用及模拟实现

  • 1.stack的使用
  • 2.queue的使用
  • 3.栈和队列OJ题
    • 3.1题目1:最小栈
    • 3.2题目2:栈的压入、弹出序列
    • 3.3题目3:逆波兰表达式求值
    • 3.4题目4:用栈实现队列
  • 4.栈的模拟实现
  • 5.队列的模拟实现
  • 6.deque
    • 6.1什么是deque
    • 6.2deque的底层实现
    • 6.3deque迭代器的讲解
    • 6.4库中deque实现细节
  • 7.prioriry_queue的介绍和使用
    • 7.1什么是priority_queue
    • 7.2在OJ题中的使用
    • 7.3priority_queue的模拟实现
      • 7.3.1仿函数
        • 7.3.1.1仿函数的其它应用场景

1.stack的使用

有了前面的基础之后,栈和队列的使用都很简单,而且其中的接口我们在数据结构中都实现过,所以我们直接看一遍即可:

在这里插入图片描述
不同的是,栈中没有实现迭代器,这其实与栈和队列的实现有关:
在这里插入图片描述
那么它的访问方式只有循环出栈和获取栈顶元素:

#include <stack>
int main()
{stack<string> s1;s1.emplace("hello world!");s1.emplace("Xxxxxxxxxxxxx");while (!s1.empty()){cout << s1.top();s1.pop();}cout << endl;//Xxxxxxxxxxxxxhello world!cout << s1.empty() << endl;//1,表示此时栈为空return 0;
}

2.queue的使用

queue的使用和stack相同,这里不再看:

在这里插入图片描述

#include <queue>
int main()
{queue<string> q1;q1.emplace("hello world!");q1.emplace("Xxxxxxxxxxxx");while (!q1.empty()){cout << q1.front();q1.pop();}cout << endl;//hello world!Xxxxxxxxxxxxcout << q1.empty() << endl;//1,表示队列为空return 0;
}

3.栈和队列OJ题

3.1题目1:最小栈

链接: 最小栈
在这里插入图片描述
在这里插入图片描述

class MinStack {
public:MinStack() {//初始化堆栈操作其实不用实现://1.自己不实现,内置类型:不一定处理,自定义类型:走自己的构造函数,stack中有自己的构造函数//2.写了函数不实现:走初始化列表,没有初始化列表,判断是否给了初始值,最后走构造函数//所以我们既可以给这个函数删了,也可以置之不管,我们这里直接不管}void push(int val) {_stack.push(val);if(_minstack.empty() || val <= _minstack.top()) _minstack.push(val);}void pop() {if(_stack.top() == _minstack.top()) _minstack.pop();_stack.pop();}int top() {return _stack.top();}int getMin() {return _minstack.top();}
private://创建两个栈stack<int> _stack;stack<int> _minstack;
};

3.2题目2:栈的压入、弹出序列

链接: 栈的压入、弹出序列
在这里插入图片描述

class Solution {
public:bool IsPopOrder(vector<int>& pushV, vector<int>& popV) {if(pushV.size() != popV.size()) return false;stack<int> _stack;int pushi = 0, popi = 0;while(pushi < pushV.size()){_stack.push(pushV[pushi++]);//先入栈while(!_stack.empty() && _stack.top() == popV[popi]){//当栈顶元素和要删除的元素相等时,要出栈_stack.pop();popi++;}}return _stack.empty();//如果为空,返回true,否则返回false}
};

3.3题目3:逆波兰表达式求值

链接: 逆波兰表达式求值
在这里插入图片描述

class Solution {
public:int evalRPN(vector<string>& tokens) {//首先要先取值入栈stack<int> s1;for(int i = 0; i<tokens.size(); i++){string& s = tokens[i];//枚举数字很困难,那么我们就枚举符号//s是string类型,而s[0]是第一个字符if(s == "+" || s == "-" || s == "*" || s == "/"){//是字符的话,就拿两个数据进行运算int right = s1.top();s1.pop();int left = s1.top();s1.pop();switch(s[0]){case '+':s1.push(left + right);break;//注意break别忘记写了case '-':s1.push(left - right);break;case '*':s1.push(left * right);break;case '/':s1.push(left / right);break;}}else{//是数字,就入栈s1.push(atoi(s.c_str()));}}return s1.top();}
};

3.4题目4:用栈实现队列

链接: link
在这里插入图片描述

class MyQueue {
public:MyQueue() {}void push(int x) {//入队列就是直接向入栈中插入数据Push.push(x);}int pop() {//如果出栈中有数据,直接出栈,否则,先入栈,再出栈if(Pop.empty()){while(!Push.empty()){Pop.push(Push.top());Push.pop();}}int ret = Pop.top();Pop.pop();return ret;}int peek() {if(Pop.empty()){while(!Push.empty()){Pop.push(Push.top());Push.pop();}}return Pop.top();}bool empty() {return Push.empty() && Pop.empty();}
private://应该是要两个栈,一个是入栈,一个是出栈stack<int> Push;stack<int> Pop;
};

4.栈的模拟实现

栈的实现较为简单,我们直接来看:

//栈的模拟实现
1.我们可以按照之前数据结构讲的那样,开辟数组来实现栈
//template<class T>
//class Stack
//{
//private:
//	T* _a;
//	size_t _top;
//	size_t _capacity;
//};//但是库中的栈并不是这样实现的,而是使用了一个适配器:
namespace Mine
{template<class T, class container>class Stack{public://入栈void push(const T& x){_con.push_back(x);//因为使用了之前我们用过的容器来实例化栈,所以我们可以直接使用容器的函数来实现栈!}//出栈void pop(){_con.pop_back();}//获取栈顶元素const T& top() const{return _con.back();}//栈中的数个数size_t size() const{return _con.size();}//判空bool empty() const{return _con.empty();}private:container _con;//这时我们可以直接使用容器来创建一个对象};
}///#include "Stack.h"
int main()
{Mine::Stack<int, vector<int>> s1;s1.push(1);s1.push(2);s1.push(3);while (!s1.empty()){cout << s1.top() << " ";s1.pop();}cout << endl << s1.size();//3 2 1      0(size)return 0;
}

但是我们可能会有疑惑:为什么我们可以直接使用到std中的stack以及它的函数呢?因为:using namespace std;我们在一开始就已经展开了,如果我们不展开的话,还要加上std::,如果展开在#include "Stack.h"后面,也是没有问题的,因为stack是模板参数,当实例化时才会进行错误的检查

5.队列的模拟实现

队列的实现也比较简单,我们直接看即可:

//队列的模拟实现
//队列的实现也是这样:
namespace Mine
{//在模板定义时可以直接为模板参数赋初始值template <class T, class container = list<int>>class Queue{public://入队列void push(const T& x){_con.push_back(x);}//出队列void pop(){//对于vector来说,它没有相应的头删函数,因为头删的消耗太大了,所以容器不能够使用vector_con.pop_front();}//获取队头元素const T& front() const{return _con.front();}//获取队尾元素const T& back() const{return _con.back();}//队列大小size_t size() const{return _con.size();}//队列判空bool empty() const{return _con.empty();}private:container _con;};
}/#include <list>
#include "Queue.h"
int main()
{Mine::Queue<int, list<int>> q1;q1.push(1);q1.push(2);q1.push(3);while (!q1.empty()){cout << q1.front() << " ";q1.pop();}cout << endl;//1 2 3return 0;
}

6.deque

上面我们已经实现了栈和队列,但是我们看库中的栈和队列时,会发现:

在这里插入图片描述
库中实现的栈和队列的适配器传入的都是一个叫deque的容器,那么这个容器究竟是什么呢?这个容器的底层实现是什么呢?:

6.1什么是deque

在这里插入图片描述
我们可以看一下deque实现的功能:
在这里插入图片描述

6.2deque的底层实现

在这里插入图片描述
所以说deque的缺陷在于[]访问这里,通过代码验证可知,在对10000个数据进行排序时,使用vector比使用deque快将近两倍多,而且如果将deque中的数据拷贝到vector中进行排序,排序之后再拷贝过来的效率也要比单独在deque中进行排序快两倍左右!
所以deque的访问是一个大问题,而拷贝其实是不怎么消耗时间的

6.3deque迭代器的讲解

所以说,对于栈和队列那种不经常插入删除的容器来说,使用deque是再好不过的了

下面我们来看一下deque的迭代器是怎么实现的:
在这里插入图片描述

6.4库中deque实现细节

在这里插入图片描述

7.prioriry_queue的介绍和使用

7.1什么是priority_queue

在这里插入图片描述

翻译为优先级队列,包含在queue头文件中,可以按照数据的优先级对数据进行排序,默认是较大值,它的第一个元素总是大于它所包含的其它元素,它的使用如下:
在这里插入图片描述

int main()
{priority_queue<int> pq1;pq1.push(5);pq1.push(1);pq1.push(2);pq1.push(9);//默认是按照大的优先级排列while (!pq1.empty()){cout << pq1.top() << " ";pq1.pop();}cout << endl;//9 5 2 1return 0;
}

我们看到这个是不是感觉很熟悉,那就是堆,大堆、小堆和这个相似,所以我们可以使用堆的方法来实现这个容器

int main()
{//如果我们想要让小的数据优先级高,需要这样使用://priority_queue<int, vector<int>, less<int>> pq1;priority_queue<int, vector<int>, greater<int>> pq1;pq1.push(5);pq1.push(1);pq1.push(2);pq1.push(9);while (!pq1.empty()){cout << pq1.top() << " ";pq1.pop();}cout << endl;//1 2 5 9return 0;
}

7.2在OJ题中的使用

链接: 数组中的第k个最大元素
有了这个容器,该题目实现起来很简单:
在这里插入图片描述

class Solution {
public:int findKthLargest(vector<int>& nums, int k) {priority_queue<int> pq1;//先插入数据for(int i = 0; i<nums.size(); i++){pq1.push(nums[i]);}while(--k)//再将k前边的数据进行删除{pq1.pop();}return pq1.top();//最后只剩下数据k了,直接返回}
};

7.3priority_queue的模拟实现

模拟实现需要使用堆中的算法,所以需要先进行复习:

链接: link
向上调整算法:
在这里插入图片描述
上面的图是按照小的优先级高来画的,但是默认是大的优先级大,但是实现思路相同
在这里插入图片描述

7.3.1仿函数

我们先实现一下优先级队列的迭代器区间构造:
在这里插入图片描述

//迭代器区间构造
template<class Inputiterator>
priority_queue(Inputiterator begin, Inputiterator end):_con(begin, end)
{//迭代器区间插入之后,要将_con设置成为大堆for (int i = (_con.size()-1-1)/2; i >= 0; i--){AdjustDown(i);}
}int main()
{int a[] = { 1, 5, 3, 9, 7 };//Mine::priority_queue<int> pq(a, a+sizeof(a)/sizeof(int));Mine::priority_queue<int> pq;//err:没有合适的默认构造可用while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;return 0;
}

当我们不写默认构造的话,编译器会自己提供一个默认构造,但是这里我们写了默认构造,但是不想写_con,也就是容器适配器(vector)的默认构造,有没有什么办法?:
在这里插入图片描述
这需要写这个就可以强制生成一个默认构造来使用

但是我们知道,库中的优先级队列的实现可以通过传入greater或less参数来实现优先级的更改的,那么我们如何实现这个操作呢?这时候就要看仿函数这个概念了:

template<class T>
struct Less
{bool operator()(const T& x, const T& y) const{return x < y;}
};template<class T>
struct Greater
{bool operator()(const T& x, const T& y) const{return x > y;}
};int main()
{//仿函数的使用Less<int> LessFunc;cout << LessFunc(1, 2) << endl;//1//如果我们只看LessFunc(1, 2)这个的话,很想一个函数调用,所以被称为仿函数return 0;
}

可以看出,仿函数的使用还是很简单的,而仿函数调用的原理其实是:
在这里插入图片描述
仿函数在我们实现的优先级队列中的应用为:
在这里插入图片描述
但是库中在实现优先级队列时,传入的是less,创建的是大堆,传入的是greater,创建的是小堆,所以我们也要做一下更改:
在这里插入图片描述

7.3.1.1仿函数的其它应用场景
//假设我们实现了一个日期类,其中实现了日期类的传参构造,>和<的比较,以及流插入的重载
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream& operator<<(ostream& _cout, const Date& d);
private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}int main()
{Mine::priority_queue<Date> q1;q1.push({ 2024, 10, 23 });q1.push({ 2024, 5, 27 });q1.push({ 2024, 11, 2 });while (!q1.empty()){cout << q1.top() << " ";q1.pop();}cout << endl;return 0;
}

这里我们可以看到,该程序成功实现了我们的需求,但是我们这样改一下:

int main()
{//这里传入的参数为Date*类型Mine::priority_queue<Date*> q1;q1.push(new Date{ 2024, 10, 23 });q1.push(new Date{ 2024, 5, 27 });q1.push(new Date{ 2024, 11, 2 });while (!q1.empty()){cout << *q1.top() << " ";q1.pop();}cout << endl;return 0;
}

在这里插入图片描述
这时我们看到,因为传入的是地址,而地址在new开辟时是不确定的,所以通过比较地址大小来排序是完全不行的,谁最大都有可能出现,所以我们要解决这个问题:

struct Dateless
{bool operator()(const Date* d1, const Date* d2) const{return *d1 < *d2;}
};
struct Dategreater
{bool operator()(const Date* d1, const Date* d2) const{return *d1 > *d2;}
};int main()
{//Mine::priority_queue<Date*> q1;Mine::priority_queue<Date*, vector<Date*>, Dateless> q1;q1.push(new Date{ 2024, 10, 23 });q1.push(new Date{ 2024, 5, 27 });q1.push(new Date{ 2024, 11, 2 });while (!q1.empty()){cout << *q1.top() << " ";q1.pop();}cout << endl;return 0;
}

可以看出,我们的解决方法为:再写出两个比较的类进行传入,如果我们需要比较的是两个指针的话,那么走的就是这个解引用的函数,所以仿函数的优点在于:如果库中不支持这个比较,而且我们访问不到这个类时,我们就可以自己写一个仿函数,实现我们想要的需求,但是如果我们不像每次比较时都要传参的话要怎么办呢?:这个之后会讲到!
我们现在还要看的一个是sort的传参和模板参数的问题:
在这里插入图片描述

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

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

相关文章

103、QT搭建Excel表环境-使用Qtxlsx库

环境搭建 文件下载 下载QtXlsx源码&#xff1a;https://github.com/dbzhang800/QtXlsxWriter 下载的内容里面的目录结构如下&#xff1a; 搭建perl环境 官网链接: https://strawberryperl.com/ 下载后并安装 检验是否有perl环境的方法&#xff1a; perl --version安装前检…

病毒分析-PEID查壳工具

病毒分析-PEID查壳工具 PEID是一款强大的查壳工具&#xff0c;广泛应用于IT安全领域中的恶意软件分析、逆向工程等领域&#xff0c;&#xff0c;其原理主要是通过对PE&#xff08;Portable Executable&#xff09;文件的头部信息、导入表、导出表等关键区域进行扫描&#xff0…

Go语言基础教程:闭包

在这篇教程中&#xff0c;我们将通过一段简单的 Go 语言代码来理解闭包的概念。闭包是编程中非常强大且常用的工具&#xff0c;尤其适合实现像计数器这样的逻辑。我们将逐行讲解代码&#xff0c;并理解如何在 Go 中利用闭包来保存函数状态。 package mainimport "fmt&quo…

使用QT绘图控件QCustomPlot绘制波形图

使用QT绘图控件QCustomPlot绘制波形图 下载QCustomPlot 下载QCustomPlot,链接路径 解压之后就能看到源代码了 在Qt中添加QCustomPlot的帮助文档 在Qt Creator的菜单:工具–>选项–>帮助–>文档–>添加qcustomplot\documentation\qcustomplot.qch文件。

LeetCode:2747. 统计没有收到请求的服务器数目(滑动窗口 Java)

目录 2747. 统计没有收到请求的服务器数目 题目描述&#xff1a; 实现代码与解析&#xff1a; 滑动窗口 原理思路&#xff1a; 2747. 统计没有收到请求的服务器数目 题目描述&#xff1a; 给你一个整数 n &#xff0c;表示服务器的总数目&#xff0c;再给你一个下标从 0 开…

提升产品竞争力之--IPD产品成本篇

在汉捷的咨询过程中&#xff0c;很多企业老总交流时都会提起这个抱怨&#xff1a;“现在产品竞争太激烈了&#xff0c;客户买产品首先看价格&#xff0c;你价格高一点就买别家的啦……” 汉捷咨询在前文谈到“通过定义产品包需求&#xff0c;来提升产品竞争力。差异化开发&…

【OpenAI】第二节(Token)关于ChatGPT的Token你了解多少?最全Token讲解过程!

在当今的人工智能领域&#xff0c;GPT&#xff08;Generative Pre-trained Transformer&#xff09;无疑是最受关注的技术之一。无论是在文本生成、对话系统&#xff0c;还是在内容创作中&#xff0c;GPT都展现出了强大的能力。然而&#xff0c;很多人对GPT的工作原理仍然存在疑…

MobileViT模型实现图像分类

项目源码获取方式见文章末尾&#xff01; 回复暗号&#xff1a;13&#xff0c;免费获取600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 **《------往期经典推荐------》**项目名称 1.【Bi-LSTM-CRF实现中文命名实体识别工具(TensorFlow)】 2.【卫星图像道路检测…

跨界创新|使用自定义YOLOv11和Ollama(Llama 3)增强OCR文本识别

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

渗透测试导学

内容预览 ≧∀≦ゞ 渗透测试导学什么是渗透测试&#xff1f;安全服务&#xff08;安服&#xff09;与红队的区别常见渗透测试相关认证渗透测试的关键步骤打点阶段1. 信息搜集2. 漏洞扫描3. 漏洞挖掘 渗透阶段1. 权限维持&#xff08;持久化&#xff09;2. 权限提升3. 免杀与隐藏…

DevOps实践:在GitLab CI/CD中集成静态分析Helix QAC的工作原理与优势

基于云的GitLab CI/CD平台使开发团队能够简化其CI/CD流程&#xff0c;并加速软件开发生命周期&#xff08;SDLC&#xff09;。 将严格的、基于合规性的静态分析&#xff08;如Helix QAC所提供&#xff09;作为新阶段添加到现有的GitLab CI/CD流程中&#xff0c;将进一步增强SD…

如何使用 NumPy 和 Matplotlib 进行数据可视化

如何使用 NumPy 和 Matplotlib 进行数据可视化 在数据科学领域&#xff0c;NumPy 和 Matplotlib 是 Python 中最常用的两个库。NumPy 用于科学计算和数据处理&#xff0c;而 Matplotlib 提供了丰富的图表工具来展示数据。本文将介绍如何将这两个库结合使用&#xff0c;轻松进行…

现货黄金怎么交易能快速入门?

现货黄金交易的核心在于以小博大&#xff0c;即用较小的亏损去搏击较大的利润&#xff0c;成功不仅要靠资金上的管理&#xff0c;更需要心态和策略的支持。现货黄金交易的过程也是人性修炼的过程&#xff0c;新手投资者不仅要学会交易技巧&#xff0c;更需要学会控制情绪&#…

sql server 行转列及列转行

图1 图2 1.行转列 &#xff08;图1->图2&#xff09; 1.方法一 (数据库通用&#xff09;&#xff0c;使用max 加case when 函数 -- 行转列 图1->图2 SELECT name,MAX(CASE WHEN subject语文 THEN score ELSE 0 END) AS "语文",MAX(CASE WHEN subject数学 …

Python的pickle模块

pickle 是 Python 标准库中的一个模块&#xff0c;用于对象的序列化&#xff08;serialization&#xff09;和反序列化&#xff08;deserialization&#xff09;。 序列化是将对象转换为字节流的过程&#xff0c;而反序列化则是从字节流恢复对象的过程。 通过 …

雷池社区版有多个防护站点监听在同一个端口上,匹配顺序是怎么样的

如果域名处填写的分别为 IP 与域名&#xff0c;那么当使用进行 IP 请求时&#xff0c;则将会命中第一个配置的站点 以上图为例&#xff0c;如果用户使用 IP 访问&#xff0c;命中 example.com。 如果域名处填写的分别为域名与泛域名&#xff0c;除非准确命中域名&#xff0c;否…

深入剖析MySQL的索引机制及其选型

在数据库管理系统中&#xff0c;索引是一种重要的优化工具&#xff0c;用于加速数据的检索和查询处理。在MySQL中&#xff0c;合理使用索引可以显著提高数据库的性能。本文将深入探讨MySQL的索引机制&#xff0c;包括不同类型索引的优势、劣势及在实际使用中的选型策略。 1. 什…

将后端返回的网络url转成blob对象,实现pdf预览

调用e签宝返回的数据是网络链接就很让人头疼&#xff0c;最后想到可以转换成blob对象&#xff0c;便在百度上找到方法&#xff0c;记录一下。 祝大家节日快乐&#xff01;&#xff01; 代码在最后&#xff01;&#xff01;&#xff01;&#xff01; 代码在最后&#xff01;&a…

Yandex搜索广告开户与投放全攻略!

Yandex 是俄罗斯最大的搜索引擎与数字广告平台&#xff0c;在俄罗斯市场具有广泛的影响力和庞大的用户基础。以下是 Yandex 搜索广告开户与投放的全攻略&#xff0c;包括云衔科技支持的相关服务。 一、Yandex 搜索广告的优势 1、广泛的市场覆盖&#xff1a;Yandex 在俄罗斯的…

Git合并多个分支中的提交内容

IDEA中使用 IEAD编辑器中使用Git IEAD编辑器中使用Git 案例一&#xff1a; 把test分支的其中提交的内容合并到main分支上。 你现在通过IDEA开发的分支是test分支&#xff0c;当你在test分支把内容都写完了并且提交内容保存到了本地的git暂存区中的时候&#xff0c;如果此时你的…