C++ queue priority_queuestack 详解及模拟实现

1. stack的介绍和使用

1.1 stack的介绍

1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。
2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。
3. stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下
操作:

  • empty:判空操作
  • back:获取尾部元素操作
  • push_back:尾部插入元素操作
  • pop_back:尾部删除元素操作

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

5.不支持迭代器:要保证先进先出。


1.2 stack的使用

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

代码示例:

void test_stack1()
{stack<int> st;st.push(1);st.push(2);st.push(3);st.push(4);while (!st.empty()){cout << st.top() << " ";st.pop();}cout << endl;
}



2.模拟实现stack  

        从栈的接口中可以看出,栈实际是一种特殊的vector,因此使用vector完全可以模拟实现stack。
    适配器模式--转换(在这里我们使用vector就行转换
    为了代码的灵活性(fanx,利用模板接收不同的容器
    函数参数传递的是对象,而模板参数传递的是类型,模板参数也是可以缺省的

namespace mystack {template<class T, class Container = vector<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;};
}

测试:

	void test(){stack<int> st;st.push(1);st.push(2);st.push(3);st.push(4);while (!st.empty()){cout << st.top() << " ";st.pop();}cout << endl;}

测试结果:


3. queue的介绍和使用

3.1queue的介绍
1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素。
2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。
3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:

  • empty:检测队列是否为空
  • size:返回队列中有效元素的个数
  • front:返回队头元素的引用
  • back:返回队尾元素的引用
  • push_back:在队列尾部入队列
  • pop_front:在队列头部出队列

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

3.2queue的使用

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

示例:

void test_queue()
{queue<int> qe;qe.push(1);qe.push(2);qe.push(3);qe.push(4);while (!qe.empty()){cout << qe.front() << " ";qe.pop();}cout << endl;
}


4.queue模拟实现前言 

因为queue的接口中存在头删和尾插,因此使用vector来封装效率太低,因为我们直接使用库中queue默认的容器deque(双端队列)来实现。


5.deque的简单介绍

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

deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:

双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:
那deque是如何借助其迭代器维护其假想连续的结构呢


5.1deque的缺陷 

与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。

与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。

但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构。
 


5.2 为什么选择deque作为stack和queue的底层默认容器

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stack和
queue默认选择deque作为其底层容器,主要是因为:

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

结合了deque的优点,而完美的避开了其缺陷。

 


6.模拟实现queue 

这里我们就使用deque作为底层默认容器来实现queue了

namespace bit
{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;};

测试:

	void testmy_queue(){queue<int> qe;qe.push(1);qe.push(2);qe.push(3);qe.push(4);while (!qe.empty()){cout << qe.front() << " ";qe.pop();}cout << endl;}

测试结果:

当然你也可以使用list作为底层容器:

	void testmy_queue(){queue<int,list<int>> qe;qe.push(1);qe.push(2);qe.push(3);qe.push(4);while (!qe.empty()){cout << qe.front() << " ";qe.pop();}cout << endl;}


7. priority_queue


7.1仿函数 

在学习priority queue之前我们先来了解一下仿函数

什么是仿函数(函数对象)?

        仿函数就是假函数,它是把对象当作函数使用,所以也称为函数对象。因为普通函数在某些特殊场景下使用比较麻烦,所以就诞生了仿函数。

        如何实现仿函数?

        重载()运算符即可。

 实现仿函数
        代码中定义Less类,重载(),函数中定义了a、b两个参数,当a小于b就返回true,否则返回false。

        在main函数中创建了Less类的对象,如果想要调用重载(),常规的调用方法应该是对象名.函数名(参数列表)。但因为重载()函数是可以省略.operator()的,所以我们可以使用简化的方式调用,这样是不是看起来跟使用普通函数一模一样了。这就是仿函数的使用。

template<class T>
class Less
{
public:bool operator()(const T& x, const T& y){return x < y;}
};int main()
{Less<int> lessfunc;cout << lessfunc(1, 2) << endl;return 0;
}


7.2priority_queue的使用和介绍 

7.2.1 priority_queue的介绍
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来自动完成此操作。

7.优先级队列的参数很重要,因为这里有很多东西要用到,比如仿函数。当然这里只看一些常用的。

priority_queue类模板参数
        这是priority_queue类的模板参数列表,里面有三个参数:

class T,class Container = vector<T>,class Compare = less<typename 
Container::value_type> 

在实现建立大堆或者小队的时候就存在着比较逻辑,那么如何来控制呢,这里就涉及到了仿函数的知识了,在模拟实现的时候再做说明。

7.2.2 priority_queue的使用
优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆。
 

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

示例:

1.建大堆

void test1()
{priority_queue<int>a;//默认是大堆//大堆的完整写法//priority_queue<int,vector<int>,less<int>>a;int n = 0;cin >> n;int x;for (int i = 0; i < n; i++) {cin >> x;a.push(x);}while (!a.empty()) {cout << a.top() << " ";a.pop();}
}

2.建立小堆

        因为priority_queue是模板,所以创建对象时需要传入模板参数,但是由于模板参数内部是具有默认值的,所以创建大堆时可以只传递元素类型即可。但创建小堆的时候,模板参数是不可以省略的。

void test2()
{priority_queue<int, vector<int>,greater<int>>a;int n = 0;cin >> n;int x;for (int i = 0; i < n; i++) {cin >> x;a.push(x);}while (!a.empty()) {cout << a.top() << " ";a.pop();}
}


 8.模拟实现priority_queue

优先级队列就是用来创建大堆和小堆的,实现优先级队列实际上就是实现堆。一般情况下:

我们建堆使用向上调整算法,删除根节点的时候我们使用向下调整算法。

(关于堆详情请看这篇文章:如果对堆不了解一定要看一下数据结构与算法--特殊的完全二叉树--堆,堆排序,利用堆解决topk的问题-CSDN博客)

通过改变这两种算法的比较逻辑,我们就可以控制是建大堆还是建小堆了。这里我们就需要用到仿函数了,仿函数在这里的作用就是控制比较逻辑,仿函数作为类就可以通过模板参数进行传递,进而控制类中需要进行比较的部分了。

class mypriorityqueue
{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;}};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[parent], _con[child])){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] < _con[child + 1])if (child + 1 < _con.size() && com(_con[child], _con[child + 1])){++child;}//if (_con[child] > _con[parent])//if (_con[parent] < _con[child])if (com(_con[parent], _con[child])){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;};
};

这里以向上调整算法为例:

		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[parent], _con[child])){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}

        conpare作用一个类型,com作为它的对象,这个对象可以像函数一样去使用,完成比较逻辑。传less在这里的意思就是,如过父亲小于孩子,那么就执行交换孩子和父亲的操作,这就是建立大堆的比较逻辑,反之greater就是建立小堆的逻辑了。

测试:建一个小堆

	void test(){priority_queue<int, vector<int>,greater<int>>a;int n = 0;cin >> n;int x;for (int i = 0; i < n; i++) {cin >> x;a.push(x);}while (!a.empty()) {cout << a.top() << " ";a.pop();}}

测试结果:正确!

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

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

相关文章

大模型日报2024-04-15

大模型日报 2024-04-15 大模型资讯 亚马逊再投资27.5亿美元加强与Anthropic合作推进生成式AI技术 摘要: 亚马逊宣布对人工智能公司Anthropic增加27.5亿美元的投资&#xff0c;旨在加深双方的合作关系。这笔投资将用于推动生成式人工智能技术&#xff0c;特别是大型语言模型的发…

JVM垃圾回收与算法

1. 如何确定垃圾 1.1 引用计数法 在 Java 中&#xff0c;引用和对象是有关联的。如果要操作对象则必须用引用进行。因此&#xff0c;很显然一个简单 的办法是通过引用计数来判断一个对象是否可以回收。简单说&#xff0c;即一个对象如果没有任何与之关 联的引用&#xff0c;即…

Leetcode 3107. Minimum Operations to Make Median of Array Equal to K

Leetcode 3107. Minimum Operations to Make Median of Array Equal to K 1. 解题思路2. 代码实现 题目链接&#xff1a;3107. Minimum Operations to Make Median of Array Equal to K 1. 解题思路 这一题思路上其实也比较直接&#xff0c;首先要使得中位数恰好为 k k k&am…

Python pyglet制作彩色圆圈“连连看”游戏

原文链接&#xff1a; Python 一步一步教你用pyglet制作“彩色方块连连看”游戏(续)-CSDN博客文章浏览阅读1.6k次&#xff0c;点赞75次&#xff0c;收藏55次。上期讲到相同的色块连接&#xff0c;链接见&#xff1a; Python 一步一步教你用pyglet制作“彩色方块连连看”游戏-…

Ai2024安装包(亲测可用)

目录 一、软件简介 二、软件下载 一、软件简介 Adobe illustrator&#xff0c;常被称为“AI”&#xff0c;是一种应用于出版、多媒体和在线图像的工业标准矢量插画的软件。作为一款非常好的矢量图形处理工具&#xff0c;该软件主要应用于印刷出版、海报书籍排版、专业插画、多…

Fiddler抓包工具之高级工具栏中的Inspectors的使用

高级工具栏中的Inspectors的使用 Inspectors 页签允许你用多种不同格式查看每个请求和响应的内容。JPG 格式使用 ImageView 就可以看到图片&#xff0c;HTML/JS/CSS 使用 TextView 可以看到响应的内容。Raw标签可以查看原始的符合http标准的请求和响应头。Cookies标签可以看到…

citus 之一 make 安装

os: centos 7.9.2009 citus: v12.1 OS 安装依赖包 sudo cd /etc/yum.repos.d sudo mkdir bak sudo mv *.repo ./baksudo wget -P /etc/yum.repos.d/ http://mirrors.aliyun.com/repo/Centos-7.repo; sudo wget -P /etc/yum.repos.d/ http://mirrors.aliyun.com/repo/epel-7.re…

手机拍照技术

拍照技巧 说明: 本文将主要介绍摄影和手机常见技巧&#xff1b; 1. 摄影的基本知识 **说明&#xff1a;**关于摄影&#xff0c;手机和相机的原理都是相同的&#xff0c;不同的是相机在很多方面优于手机&#xff0c;但是专业的设备对于我们这种的非专业的人来说&#xff0c;刚…

乾坤微前端js沙箱机制

1 快照沙箱 modifyPropsMap对象存储子应用的属性&#xff1b; windowSnapshot对象存储微应用未加载时的window对象属性&#xff1b;进入微应用&#xff0c;利用windowSnapshot对象存储window对象的属性&#xff1b; 并将window对象的属性替换为modifyPropsMap对象的属性&#x…

Linux时间同步练习

题目如下&#xff1a; 一.配置server主机要求如下&#xff1a; 1.server主机的主机名称为 ntp_server.example.com 2.server主机的IP为&#xff1a; 172.25.254.100 3.server主机的时间为1984-11-11 11&#xff1a;11&#xff1a;11 4.配置server主机的时间同步服务要求可以被所…

重磅,巫师3即将发布mod编辑器并开放创意工坊

热乎乎的消息&#xff01;巫师3即将推出mod编辑器和开放创意工坊&#xff01; 根据巫师3官方Steam消息&#xff0c;听说年底将推出mod编辑器&#xff0c;目前已经开始内测。想试用的玩家们&#xff0c;可以到redkit商店页面申请访问权限&#xff0c;体验最新的创意工具。 此外&…

NameError: name ‘init_detector’ is not defined

使用模型提取人体pose&#xff0c;遇到的问题记录。 1. 排查问题直接讲报错的地方拷贝在python中直接运行。 运行后提示&#xff1a; ModuleNotFoundError: No module named mmcv._ext 经过各种的地方去查找问题 github的issue Error in init_detector Issue #3354 ope…

存入Redis的值前面有很多空格

说明&#xff1a;记录一次使用Redis的错误&#xff1b; 场景 在将验证码存入Redis时&#xff0c;发现存入的值前面有很多空格&#xff0c;导致在与前端传入的值比较时&#xff0c;一直是false&#xff0c;验证不通过。如下&#xff1a; 上面这些“□”是占位符&#xff0c;复…

STM32单片机中TogglePin和WritePin的区别及使用方法

目录 1.区别 2.使用方法 3. HAL_GPIO_TogglePin函数 4.HAL_GPIO_WritePin函数 在STM32单片机中&#xff0c;WritePin用于将引脚设置为特定电平&#xff0c;而TogglePin用于切换引脚的电平。 1.区别 TogglePin是切换引脚电平状态&#xff0c;即引脚电平状态在高电平和低电…

学习笔记(4月17日)vector底层原理

1.vector<vector>底层原理 vector是表示可变大小数组的序列容器&#xff0c;相当于一个动态的数组&#xff0c;比数组优越的在于它具有可动态改变的大小&#xff0c;同时&#xff0c;它写成了类模板&#xff0c;说明可以适用于其他类型&#xff0c;包括vector本身&#…

Oracle数据库故障类别及日常运维规划策略

一、故障类别 1、语句故障 单个数据库操作失败&#xff08;select、insert、update或delete&#xff09;&#xff0c;如&#xff1a; 在表中输入无效的数据&#xff0c;解决方法&#xff1a;可与用户合作来验证并更正数据&#xff1b;执行操作&#xff0c;但权限不足&#x…

rust 学习笔记(13-19)

13 迭代器与闭包 Rust 的设计灵感来源于很多现存的语言和技术。其中一个显著的影响就是 函数式编程&#xff08;functional programming&#xff09;。函数式编程风格通常包含将函数作为参数值或其他函数的返回值、将函数赋值给变量以供之后执行等等。 闭包&#xff08;Closu…

游戏、app抓包

文章目录 协议app抓包游戏抓包 协议 在抓包之前&#xff0c;首先我们要对每个程序使用什么协议有个大致的了解&#xff0c;比如网页这种就是走的http协议。 在一些app中我们通过发送一个请求&#xff0c;然后服务器接受&#xff0c;响应&#xff0c;返回一个数据包&#xff0c…

4.17freeRTOS

1.总结串口的发送和接收功能使用到的函数 串口的数据发送 HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout) UART_HandleTypeDef *huart&#xff1a;指定要使用的串口 const uint8_t *pData&#xf…

【Python-基础】列表合集

.tolist() 将其他数据类型转换成列表 1.可以将<class ‘pandas.core.series.Series’>转换为列表 images [img_id[filename].tolist() for img_id in data] # data是一个列表,列表中每一项都是用read_csv函数读取的csv文件内容 # type(img_id[filename]) <class …