C++的stack和queue类(一):适配器模式、双端队列与优先级队列

news/2024/5/5 13:00:34/文章来源:https://blog.csdn.net/m0_73975164/article/details/137441564

目录

基本概念

stack的使用

queue的使用

适配器模式       

stack.h

test.cpp

双端队列-deque

仿函数

优先队列

priority_queue的使用

queue.h文件

stack.h文件

test.cpp文件

日期类的比较

商品的比较

结论 


基本概念

1、stack和queue不是容器而是容器适配器,它们没有迭代器

2、适配stack和queue的默认容器是deque,因为:

  1. stack和queue不需要遍历,只需要在固定的一端或者两端进行操作
  2. 在stack中元素增加需要扩容时,deque比vector的效率高(不需要搬移大量数据)
  3. queue中的元素增长时,deque不仅效率高,而且内存使用率高

stack的使用

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

queue的使用

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

适配器模式       

        适配器模式是一种设计模式,用于将一个类的接口转换成客户希望的另一个接口,这种类型的设计模式属于结构型模式,它涉及到单个类的功能增强,适配器模式中有三个主要角色:

  • 目标接口:客户端所期待使用的接口,适配器通过实现这个目标接口来与用户进行交互
  • 被适配者:需要被适配以符合目标接口规范的现有类
  • 适配器:实现了目标接口,并持有一个对被适配者对象的引用,在其方法内部调用被适配者对象来完成特定操作

stack.h

#pragma once
#include <assert.h>
#include <vector>
#include <list>
namespace bit 
{//适配器模式//stack<int,vector<int>> st1;//stack<int,list<int>> st2;template<class T, class Container>class stack{public://入栈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(){return _con.back();}private:Container _con;};
}
  • 目标接口:构成栈所需的操作接口
  • 被适配者:实现栈的底层数据结构(数组或链表) 
  • 适配器:bit::stack类

test.cpp

#include "Queue.h"
#include "Stack.h"
#include <stack>
#include <iostream>
using namespace std;void test_stack1()
{bit::stack<int,vector<int>> st;st.push(1);st.push(2);st.push(3);st.push(4);while (!st.empty()){cout << st.top() << " ";st.pop();}cout << endl;}int main()
{test_stack1();return 0;
}

注意事项:函数参数传递的是对象,模板参数传递的是类型,函数参数可以传递缺省值,模板参数也可以传递缺省值

template<class T, class Container = vector<int>>
bit::stack<int> st; //此时就等价于bit::stack<int,vector<int>> st

双端队列-deque

vector优缺点 

  • 优点:支持下标随机访问
  • 缺点:头部或中间插入删除效率低,扩容有消耗

list的优缺点:

  • 优点:任意位置插入删除效率都不错
  • 缺点:不支持下标的随机访问

(第一个stack和queue的2:30:00处)

基本概念:deque是一种双开口的”连续“空间的数据结构,与vector相比,头插效率高,不需要搬移元素,与list相比,空间利用率更高,deque不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组

优点:deque 允许在两端进行高效插入和删除操作,且支持下标的随机访问

缺点:中间插入删除效率一般、[]效率一般(遍历时deque要频繁的检查是否移动到小空间边界)

​ 

仿函数

基本概念:仿函数是一个类或结构体,它重载了函数调用运算符 operator(),通过重载该运算符,这个类的实例就可以被像函数一样调用

#include <iostream>//仿函数 + 函数模板
template <class T>
struct MyComparator 
{bool operator()(const T& x,const T& y) {return x < y;}
};int main() {MyComparator<int> cmp;cout<< cmp(1, 2) << endl;//有名对象cout<< cmp.operator()(1, 2) << endl;//有名对象cout<< MyComparator<int>()(1, 2) << endl;//匿名对象cout<< MyComparator<int>().operator()(1, 2) << endl;//匿名对象return 0;
}

优先队列

文档:cplusplus.com/reference/queue/priority_queue/

基本概念

1、优先队列是一种容器适配器

2、优先队列的底层容器的虚拟逻辑是一个堆,只能获取堆顶元素

3、底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作 (标准容器类vector和deque满足以下需求)
  • empty():检测容器是否为空
  • size():返回容器中有效元素个数
  • front():返回容器中第一个元素的引用
  • push_back():在容器尾部插入元素
  • pop_back():删除容器尾部元素

5. 默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector
注意事项:
1、默认情况下,priority_queue是大堆
#include <vector>
#include <queue>
#include <functional> // greater算法的头文件
void TestPriorityQueue()
{// 默认情况下,创建的是大堆,其底层按照小于号比较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;
}int main()
{TestPriorityQueue();return 0
}
2. 若在priority_queue中放自定义类型的数据,需要在自定义类型中提供> 或者< 的重载

priority_queue的使用

函数声明接口说明
priority_queue
构造一个空的优先级队列
empty
检测优先级队列是否为空
top
返回优先级队列中最大(最小元素),即堆顶元素
push
在优先级队列中插入元素x
pop
删除优先级队列中最大(最小)元素,即堆顶元素

queue.h文件

#pragma once
#include<queue>
#include <stack>
#include<algorithm>
#include<vector>
#include<list>
#include <string>
#include <iostream>
using namespace std;namespace bit
{//基于deque实现的队列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();}//获取队头const T& front(){return _con.front();}//获取队尾const T& back(){return _con.back();}private:Container _con;};//实现小堆逻辑的仿函数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;}};//优先级队列(底层数据结构是vector,比较部分调用了仿函数)template<class T, class Container = vector<T>, class Compare = less<T>>//不传入指的大小堆实现逻辑则按默认实现小堆逻辑class priority_queue{public://向上调整void adjust_up(size_t child){Compare com;int parent = (child - 1) / 2;while (child > 0){//if (_con[child] > _con[parent]):求大堆逻辑:将孩子与父亲间大的向上移动//if (_con[parent] < _con[child]):求小堆逻辑:将孩子与父亲间小的向前移动if (com(_con[child],_con[parent]))//实现大堆还是小队基于使用的仿函数{swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{ break;}}}void push(const T& x){_con.push_back(x);//直接调用现有的尾插函数adjust_up(_con.size() - 1);//向上调整建(大/小)堆}void adjust_down(size_t parent){Compare com;size_t child = parent * 2 + 1;//不到叶子就继续while (child < _con.size()){//找出最大的那个孩子//if (child + 1 < _con.size() && _con[child + 1] > _con[child])//大堆逻辑//if (child + 1 < _con.size() && _con[child + 1] > _con[child])//小堆逻辑if (child + 1 < _con.size() && com(_con[child + 1], _con[child])){++child;}//if (_con[child] > _con[parent])//大堆逻辑//if (_con[child] < _con[parent])//小堆逻辑if (com(_con[child], _con[parent])){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}//删除堆顶元素void pop(){swap(_con[0], _con[_con.size() - 1]);//交换堆顶元素和最后一个元素_con.pop_back();//直接用现有的尾删函数adjust_down(0);//向下调整建(大/小)堆}//判空bool empty(){return _con.empty();}//获取堆中元素个数size_t size(){return _con.size;}//获取堆顶元素const T& top(){return _con[0];}private:Container _con;};	
}

stack.h文件

#pragma once
namespace bit
{//基于双端队列实现的栈template<class T, class Container = deque<T>>class stack{public: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(){return _con.back();}private:Container _con;};
}

test.cpp文件

#include"Stack.h"
#include"Queue.h"//尝试基于deque实现的队列
void test_queue()
{bit::queue<int> q;q.push(1);q.push(2);cout << q.front() << " ";q.pop();q.push(3);q.push(4);while (!q.empty()){cout << q.front() << " ";q.pop();}cout << endl;
}//优先级队列
void test_priority_queue()
{bit::priority_queue<int,vector<int>,bit::greater<int>> pq;//存放int类型的数据,底层数据结构为vector,实现大堆的逻辑pq.push(2);pq.push(1);pq.push(3);pq.push(4);pq.push(5);pq.push(7);pq.push(8);pq.push(6);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;
}int main()
{//test_queue();test_priority_queue();return 0;
}

  • 实例化优先级队列类类型的对象pq时会将默认的less<T>变为指定的greater<T>
  • 向pq中尾插2,_con是pq对象的底层数据结构vector类类型的对象,_con[size-1]可以获取2
  • 尾插后为了维护大/小堆性质要调用向上调整函数(新插入的元素后进行向上调整)
  • 向上调整函数会实例化一个greater类类型的对象com
  • 将vector中父子下标存放的数据放入com中进行比较并返回比较结果
  • 根据比较结果将父子下标在vector中存放的数据进行交换

注意事项:vs2022报内部编译器错误一般是涉及模板的内容出错

日期类的比较

#pragma once
#include<queue>
#include <stack>
#include<algorithm>
#include<vector>
#include<list>
#include <string>
#include <iostream>
using namespace std;//日期类
class Date
{
public:friend ostream& operator<<(ostream& _cout, const Date& d);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);}
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}//处理指针比较的仿函数
class GreaterPDate
{
public:bool operator()(const Date* p1, const Date* p2){return *p1 > *p2;}
};//向优先级队列中放入自定义数据
void test_priority_queue2()
{bit::priority_queue<Date, vector<Date>, bit::greater<Date>> pq;Date d1(2024, 4, 8);pq.push(d1);//有名对象pq.push(Date(2024, 4, 10));//匿名对象pq.push({ 2024, 2, 15 });//多参数隐式类型转换while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;//向优先级队列中存放指针bit::priority_queue<Date*, vector<Date*>, GreaterPDate> pqptr;//不能对内置类型Date*进行重载operator<pqptr.push(new Date(2024, 4, 14));//new了一个日期类类型的匿名对象,返回的类型是地址,如果仅根据传入的地址进行建堆则每次的值是不一样的pqptr.push(new Date(2024, 4, 11));pqptr.push(new Date(2024, 4, 15));while (!pqptr.empty()){cout << *(pqptr.top()) << " ";pqptr.pop();}cout << endl;
}int main()
{test_priority_queue2();return 0;
}

向优先级队列中传入自定义类型的对象数据:

  • 实例化优先级队列类类型的对象pq,less变greater,vector中存放的是日期类类型的数据
  • 尾插日期类类型的对象(会先调用日期类构造一个日期类类型的对象)
  • 向上调整
  • 实例化一个greater类类型的对象com
  • 传数据比较
  • 对于自定义类型数据的比较还会调用该自定义函数的比较运算符重载,最后返回比较结果

向优先级队列中传入指针类型的数据:

原因:内置类型不能进行运算符重载(operator<(const Date* d1, const Date* d2)错误)

解决办法:实现一个用于指针比较的仿函数

商品的比较

#pragma once
#include<queue>
#include <stack>
#include<algorithm>
#include<vector>
#include<list>
#include <string>
#include <iostream>
using namespace std;struct Goods
{string _name; // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};//根据单价建小堆的仿函数
struct ComparePriceLess
{bool operator()(const Goods& gl, const Goods& gr){return gl._price < gr._price;}
};//根据单价建大堆的仿函数
struct ComparePriceGreater
{bool operator()(const Goods& gl, const Goods& gr){return gl._price > gr._price;}
};//根据个数建小堆的仿函数
struct CompareEvaluateLess
{bool operator()(const Goods& gl, const Goods& gr){return gl._evaluate < gr._evaluate;}
};//根据个数建大堆的仿函数
struct CompareEvaluateGreater
{bool operator()(const Goods& gl, const Goods& gr){return gl._evaluate > gr._evaluate;}
};int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };sort(v.begin(), v.end(), ComparePriceLess());//按照单价排小堆sort(v.begin(), v.end(), ComparePriceGreater());//按照单价排大堆sort(v.begin(), v.end(), CompareEvaluateLess());按照个数排小堆sort(v.begin(), v.end(), CompareEvaluateGreater());//按照个数排大堆
}
  •  sort的函数原型:sort(begin,end,cmp)

不写打印函数记得用断点+调试

结论 

应该是自定义类型的对象 

        拿日期类来说,pq是优先级队列的对象,_con是vector类类型的对象,该对象中每个下标处存放的都是Data类类型的对象,每次尾插时回去调用pq中的尾插函数先将vector中的尾下标处插入Data类类型的对象,然后将该尾下标的数值(整型数值)传递给向上调整函数,向上调整函数会将父子下标处存放的数据/对象,放入我们预先设计好的获取大/小堆逻辑的仿函数中进行比较,如果vector中父子下标中存放的是自定义类型的对象,在进入仿函数中后还会调用该自定义类型的比较运算符重载函数比较这些自定义类型的对象中的内置类型的数据,然后返回比较结果

~over~

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

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

相关文章

性能优化原则

相关链接&#xff1a;【运行环境】加载资源的形式 性能优化 1 性能优化原则 多使用内存、缓存或其他方法 减少CPU计算量&#xff0c;减少网络加载耗时 &#xff08;适用于所有编程的性能优化----空间换时间&#xff09; 2 从何入手 性能优化-让加载更快 减少资源体积&#x…

每日一题 — 最大连续 1 的个数III

解法一&#xff1a;暴力枚举 先定义left和right双指针&#xff0c;left先固定在起始位置&#xff0c;遍历right当值等于1的时候&#xff0c;直接跳过&#xff0c;等于0的时候&#xff0c;zero计数器加一当zero等于k的时候&#xff0c;就开始记录此时最大长度是多少然后left加一…

深度剖析:网络安全中的红蓝对抗策略

红蓝对抗 红蓝对抗服务方案 在蓝队服务中&#xff0c;作为攻击方将开展对目标资产的模拟入侵&#xff0c;寻找攻击路径&#xff0c;发现安全漏洞和隐患。除获取目标系统的关键信息&#xff08;包括但不限于资产信息、重要业务数据、代码或管理员账号等&#xff09;外&#x…

Python | 超前滞后分析

Nino SST Indices (Nino 12, 3, 3.4, 4; ONI and TNI) 有几个指标用于监测热带太平洋&#xff0c;所有这些指标都是基于海表温度(SST)异常在一个给定的区域的平均值。通常&#xff0c;异常是相对于30年的周期来计算的。厄尔尼诺3.4指数(Nio 3.4 index)和海洋厄尔尼诺指数(Ocea…

Chrome谷歌下载入口

​hello&#xff0c;我是小索奇 发现好多人说谷歌浏览器在哪里下载呀&#xff0c;哪里可以找到&#xff1f; 你可能会心想&#xff0c;一个浏览器你还不会下载啊&#xff1f; 还真是&#xff0c;有很多伙伴找不到下载入口&#xff0c;为什么呢&#xff1f; Bing进行搜索&am…

java程序 .exe启动nginx防止重复启动,已解决

java代码生成好的.exe启动nginx服务程序 根据nginx占用端口来解决nginx服务重复启动问题&#xff08;下面代码了解代码逻辑后根据自己的业务需求修改即可&#xff09; 代码&#xff1a; package org.example;import javax.swing.*; import java.awt.*; import java.io.*; …

C#/WPF 使用开源Wav2Lip做自己的数字人(无需安装环境)

实现效果 Speaker Wav2Lip概述 2020年&#xff0c;来自印度海德拉巴大学和英国巴斯大学的团队&#xff0c;在ACM MM2020发表了的一篇论文《A Lip Sync Expert Is All You Need for Speech to Lip Generation In The Wild 》&#xff0c;在文章中&#xff0c;他们提出一个叫做Wa…

ChatGPT 4.0报错 :“Hmm…something seems to have gone wrong.”

ChatGPT报错&#xff0c;GPT-3.5模型正常&#xff0c;GPT-4.0报错&#xff1a;“Hmm…something seems to have gone wrong.” 说明&#xff1a;嗯…好像出了什么问题。 原因&#xff1a; 部分用户在使用GPT-3.5模型时提问正常&#xff0c;GPT-4.0模型提问时&#xff0c;出现这…

Open CASCADE学习|求曲面的参数空间

在三维空间中&#xff0c;任意的曲面都可以通过特定的方法映射到一个二维参数平面上&#xff0c;从而对其进行详细的几何分析和处理。首先&#xff0c;我们需要从三维模型中提取出特定的曲面&#xff0c;这通常被称为“Face”。一个face可以被视为三维空间中的一个封闭区域&…

xss.pwnfunction-Ah That‘s Hawt

<svg/onloadalert%26%2340%3B1%26%2341%3B> <svg/>是一个自闭合形式 &#xff0c;当页面或元素加载完成时&#xff0c;onload 事件会被触发&#xff0c;从而可以执行相应的 JavaScript 函数

【日期】获取当天以及未来三天的日期和周几

// 获取当天以及未来三天的日期和周几getDates() {const today new Date();const dayOfWeek ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];const todayDate today.toDa…

专业测评:哪个平台提供大数据信用风险检测?

在贷款申请过程中&#xff0c;大数据信用评估变得越来越重要。许多人对此感到困惑&#xff0c;不清楚如何获取自己的大数据信用风险等级。本文将为您解答疑惑&#xff0c;介绍如何正确地查询和理解大数据信用报告。 市场上的大数据信用报告查询服务乱象 目前&#xff0c;市场上…

网工内推 | 安全运维、服务工程师,软考中级、CISP优先,六险一金

01 华成峰科技 招聘岗位&#xff1a;安全运维工程师 职责描述&#xff1a; 1、负责安全产品的运维管理&#xff0c;包括设备升级变更、策略配置优化、设备巡检等&#xff1b; 2、负责7*24小时安全监控与应急响应&#xff0c;包括态势感知日志监测、安全事件分析及处置等&#…

VMware vSphere虚拟化基础管理平台

VMware简介 VMware介绍 官网&#xff1a;https://www.vmware.com/cn.html VMware公司成立于1998年&#xff0c;2003年存储厂商EMC以6.35亿美元收购了VMware&#xff1b;2015年10月&#xff0c;戴尔宣布以670亿美元收购EMC。VMware公司在2018年全年收入79.2亿美元。 VMware主…

C++修炼之路之string模拟实现

目录 前言 一&#xff1a;构造函数析构函数拷贝构造函数 二&#xff1a;c_str size capacity operator operator[] 三&#xff1a;普通迭代器 const迭代器范围for 四&#xff1a;关系操作符重载 五&#xff1a;reserveresize 六&#xff1a;push_back …

【C++第三阶段】deque容器评委打分案例

以下内容仅为当前认识&#xff0c;可能有不足之处&#xff0c;欢迎讨论&#xff01; 文章目录 构造函数赋值操作大小操作插入删除数据存取排序评委评分案例描述 deque容器 双端数组&#xff0c;可以对头端插入删除操作。 如下图所示。 头部有插入删除操作&#xff0c;尾部亦然…

three.js尝试渲染gbl模型成功!(三)

参照教程&#xff1a;https://cloud.tencent.com/developer/article/2276766?areaSource102001.5&traceId88k805RaN_gYngNdKvALJ &#xff08;作者&#xff1a;九仞山&#xff09; 通过最近两天查three.js入门教程了解到 这玩应支持包括 .obj、.gltf等类型的模型结构。 g…

【微服务】------服务注册

在 微服务的基建工作 中提到过&#xff0c;在云原生、微服务时代&#xff0c;如果还是手动修改服务地址&#xff0c;是几乎不可完成的工作&#xff0c;需要一种机制完成自动上报和获取服务地址的支撑组件&#xff0c;可以保障服务的快速上线和下线&#xff0c;这就是服务注册/发…

vue vue3 手写 动态加载组件

效果展示 一、需求背景&#xff1a; # vue3 项目涉及很多图表加载、表格加载 #考虑手写一个动态加载组件 二、实现思路 通过一个加载状态变量&#xff0c;通过v-if判断&#xff0c;加载状态的变量等于哪一个&#xff0c;动态加载组件内部就显示的哪一块组件。 三、实现效果…

酷开科技 |酷开系统全视频化升级,让电视回归视频属性

随着消费升级浪潮的兴起&#xff0c;家庭互联网这一概念也在资本的注入下&#xff0c;成为了新风口。酷开系统全视频化升级&#xff0c;让电视回归视频属性&#xff0c;酷开系统在之前瀑布流板块设计的基础上&#xff0c;增加了视频流图文融合的并行界面&#xff0c;同时酷开系…