【STL】优先级队列反向迭代器详解

news2024/9/24 19:22:45

目录

一,栈_刷题必备

二,stack实现

1.什么是容器适配器

2.STL标准库中stack和queue的底层结构 

了解补充:容器——deque 

1. deque的缺陷

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

三,queue实现

1. 普通queue 

2,优先级队列(有难度)

<1>. 功能

<2>. 模拟实现

1). 利用迭代器_构造

2).仿函数

sort函数中的仿函数使用理解

四. 反向迭代器(以list为例)

2. 关于迭代器文档一个小细节 

结语


 

一,栈_刷题必备

常见接口: 

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

 简单应用,刷点题:

155. 最小栈

栈的压入、弹出序列_牛客题霸_牛客网

150. 逆波兰表达式求值

二,stack实现

思路:在C语言期间,我们可以通过链表,数组形式实现过stack,而数组形式效率更高,所以stack的实现可以直接复用vector接口,包装成栈先进后出的特性

#pragma once
#include <iostream>
#include <vector>
#include <list>
using namespace std;
namespace my_s_qu
{
	template <class T >
	class stack
	{
	public:
		void push_back(const T& x)
		{
			Data.push_back(x);
		}

		void Pop()
		{
			Data.pop_back();
		}

		T& top()
		{
			return Data.back();
		}

		const T& top() const 
		{
			return Data.back();
		}

		bool empty() const
		{
			return Data.empty();
		}

		size_t size() const
		{
			return Data.size();
		}

	private:
		vector<T> Data;
	};
}

很简单不是吗? 到这里并没有结束,我们发现,我们的栈只能通过vector实现。根据c++标准库,我们还差个适配器的实现,换句话说,我们实现的栈不支持容器适配器。 

1.什么是容器适配器

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

2.STL标准库中stack和queue的底层结构 

虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为 容器适配器,这是因为stack和队列只是对其他容器的接口进行了包装,STL中stack和queue默认使用 deque

回到我们栈的实现,我们仅支持vector模式设计,优化为成容器适配器支持所有容器,都能支持栈的先进后出的特性。(在我看来这中思想更为重要)

namespace my_s_qu
{
	template <class T, class container = deque<T> >  // 添加容器模板
	{
	public:
		void push_back(const T& x)
		{
			Data.push_back(x);
		}

......
private:
		container Data;  // 容器换成模板
	};
}

其中deque又是什么?

了解补充:容器——deque 

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

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

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

 

那deque是如何借助其迭代器维护其假想连续的结构呢? 

 

1. deque的缺陷

优势: 

与vector比较,deque的 优势是: 头部插入和删除效率高。  不需要搬移元素,效率特别高,而且在 扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
与list比较优势支持随机访问。其底层是连续空间, 空间利用率比较高,不需要存储额外字段。

缺陷:  

不适合遍历。
因为在遍历时,deque的迭代器要 频繁的去 计算每个数据的位置,导致 效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list, deque的应用并不多,而目前能看到的一个应用就是, STL用其作为stack和queue的底层数据结构

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

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构, 只要具有push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对sack和queue默认选择deque作为其底层容器,主要是因为:
1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。
2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。
结合了deque的优点,而完美的避开了其缺陷。

三,queue实现

1. 普通queue 

 queue实现,我们这次以容器适配器进行。

template <class T, class container = deque<T>>
	class queue
	{
	public:
		void push_back(const T& x)
		{
			Data.push_back(x);
		}


		void Pop()
		{
			Data.pop_front();
		}

		T& back()
		{
			return Data.back();
		}

		const T& back()  const
		{
			return Data.back();
		}

		T& front()
		{
			return Data.front();
		}

		const T& front() const
		{
			return Data.front();
		}

		bool empty() const
		{
			return Data.empty();
		}

		size_t size() const
		{
			return Data.size();
		}

	private:
		container Data;
	};

2,优先级队列(有难度)

简介:

1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
2. 优先级队列底层类似于堆,在堆中可以随时插入元素,并且只能检索 最大堆元素(优先队列中位于顶部的元素)。
3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出(Pop),其称为优先队列的顶部。
5. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector
6. 需要 支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。

<1>. 功能

priority_queue()/priority_queue(fifirst, last)      构造一个空的优先级队列
empty( )                                                                 检测优先级队列是否为空,是返回true,否则返回false
top( )                                                                      返回优先级队列中最大(最小元素),即堆顶元素
push(x)                                                                  在优先级队列中插入元素x
pop()                                                                      删除优先级队列中最大(最小)元素,即堆顶元素
pop_back()                                                            删除容器尾部元素

<2>. 模拟实现

完成优先级队列,需要用到堆方面的知识,如果堆大家不太熟悉了,建议将堆实现内容复习一遍:

详解树与二叉树的概念,结构,及实现(上篇)_花果山~~程序猿的博客-CSDN博客

 利用堆方面知识,完成最基础的框架:

namespace my_priority_queue
{
	template <class T, class container = vector<T>> 
	class priority_queue
	{
	public:
		// 自定义类型,不需要给他初始化

		void ajust_up(size_t child)
		{
			int parent;
			while (child > 0)
			{
				parent = child / 2;
				if (_pri_queue[child] > _pri_queue[parent])  // 写大堆
				{
					std::swap(_pri_queue[child], _pri_queue[parent]);
					child = parent;
					parent = child / 2;
				}
				else
				{
					break;
				}
			}
		}

		T& top()
		{
			return _pri_queue[0];
		}

		bool empty()
		{
			return _pri_queue.empty();
		}

		void push(const T& x)
		{
			_pri_queue.push_back(x);
			// 向上调整
			ajust_up(_pri_queue.size() - 1);
		}

		size_t size()
		{
			return _pri_queue.size();
		}

		void ajust_down()
		{
			size_t  parent = 0;
			size_t child = 2 * parent + 1;

			while (child < _pri_queue.size())
			{
				if (child + 1 < _pri_queue.size() && _pri_queue[child + 1] >  _pri_queue[child])
				{
					child++;
				}

				if (_pri_queue[parent] < _pri_queue[child])
				{
					std::swap(_pri_queue[parent], _pri_queue[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

		void pop()
		{
			std::swap(_pri_queue[0], _pri_queue[size() - 1]);
			// 向下调整
			_pri_queue.pop_back();
			ajust_down();
		}

	private:
		container _pri_queue;
	};
}

这里我们会有一个疑问,那我们要构建升序怎么办? 仿函数会解释

1). 利用迭代器_构造

// 自定义类型,不需要给他初始化
		priority_queue()
		{}

		template  <class newiterator>
		priority_queue(newiterator begin, newiterator end)
			: _pri_queue()
		{
			while (begin != end)
			{
				push(*begin);
				begin++;
			}
		}

2).仿函数

在该场景下,我们的优先级队列已经实现了降序的功能,那我们如何实现升序? 什么你说再写一段,改一下符号??

这里仿函数就得引出了,仿函数,那么它并不是函数,那是什么? (仿函数比如:less, greater)

 仿函数本质是一个类,根据上图中,compare 模板,说明这里的仿函数,就是用来灵活改变大小堆符号的。

我们直接实现结果:

    template <class T>
	struct less
	{
		bool operator()(const T& left, const T& right)const
		{
			return left < right;
		}
	};

	template <class T>
	struct greater
	{
		bool operator()(const T& left, const T& right)const
		{
			return left > right;
		}
	};

 这个两个类都是重载了operator(),因此我们在实例化对象后,使用:

template <class T,class compare = less<T>>

compare   t;

cout <<  t(1, 2) << endl;     

从外表来看,就像一个函数调用,但实际是,一个类的成员函数的调用

t.operator(1,2) 

 这样我们就可以直接改变仿函数的类,来控制大小堆了。 

sort函数中的仿函数使用理解

函数模板中: 

 sort函数作为行参中使用:

  

sort(s.begin, s.end, greater<int> () );   // 函数中是要做参数的,我们加个括号就是一个匿名对象

四. 反向迭代器(以list为例)

如果有小伙伴还学习普通迭代器,请参考这篇文章中的普通迭代器实现。

【STL】list用法&试做_底层实现_花果山~~程序猿的博客-CSDN博客

 参考list源码,这里直接说结果,发现源码通过借用普通迭代器来构造反向迭代器

 直接上代码:

namespace my_list
{
	template <class T>
	struct list_node
	{
		list_node(const T& data = T())
			: _data(data)
			, _next(nullptr)
			, _prv(nullptr)
		{}

		T _data;
		list_node* _next;
		list_node* _prv;
	};

	template <class T, class Ref, class Ptr>
	struct list_iterator
	{
		typedef list_node<T> Node;
		typedef list_iterator< T, Ref, Ptr> iterator;

		Node* _node;

		list_iterator(Node* node)
			: _node(node)
		{}

		bool operator!= (const iterator& it)
		{
			return _node != it._node;
		}

		bool operator==(const iterator& it)
		{
			return _node == it._node;
		}

		iterator& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		iterator& operator--()
		{
			_node = _node->_prv;
			return *this;
		}

		iterator operator++(int)
		{
			iterator tmp(*this);
			_node = _node->_next;
			return *tmp;
		}

		Ptr operator*()
		{
			return _node->_data;

		}
		Ref operator->()
		{
			return &(operator*());
		}
	};

	template <class Iterator, class Ref, class Ptr>
	struct _reverse_iterator
	{
		typedef _reverse_iterator<Iterator, Ref, Ptr>  reverse_iterator;

		Iterator _cur;
		
		_reverse_iterator(const Iterator& cur)
			: _cur(cur)
		{}

		reverse_iterator& operator++()
		{
			--_cur;
			return *this;
		}

		reverse_iterator operator++(int)
		{
			reverse_iterator temp(*this);
			--_cur;
			return temp;
		}

		reverse_iterator& operator--()
		{
			++_cur;
			return _cur;
		}

		reverse_iterator operator--(int)
		{
			reverse_iterator temp(*this);
			++_cur;
			return temp;
		}

		// != 
		bool operator!=(const reverse_iterator& end)
		{
			return _cur != end._cur;
		}

		bool operator==(const reverse_iterator&  end)
		{
			return _cur == end._cur;
		}

		// *     
		Ptr operator*() 
		{
			auto tmp = _cur;
			--tmp;
			return *tmp;
		}

		// ->
		Ref operator->()
		{
			return &(operator*());
		}
	};

	template <class T>
	class list
	{
		typedef list_node<T> Node;
	public:
		typedef list_iterator<T, T*, T&>  iterator;
		typedef list_iterator<T, const T*, const T&> const_iterator;

		typedef _reverse_iterator<iterator, T*, T&> reverse_iterator;
		typedef _reverse_iterator<const_iterator, const T*, const T&> const_reverse_iterator;

		reverse_iterator rbegin()
		{
			return reverse_iterator(end());
		}

		const_reverse_iterator rbegin() const
		{
			return const_reverse_iterator(end());
		}

		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}

		const_reverse_iterator rend() const
		{
			return const_reverse_iterator(begin());
		}


		iterator begin()
		{
			return iterator(_head->_next);
		}

		iterator end()
		{
			return iterator(_head);
		}

		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}

		const_iterator end() const
		{
			return const_iterator(_head);
		}
..... //list其他成员函数这里就不再赘述了

设计思路比较简单,本质上是复用普通迭代器的函数,其他重载函数思想跟普通函数差不多。但这里也有一个比较艺术性的设计:

 

 

那这里我们来讨论一下,这个反向迭代器是否能给vector使用??  答案是肯定的

看图:

 

结论:反向迭代器:迭代器的适配器。

 

2. 关于迭代器文档一个小细节 

那是不是所有的容器都合适呢? 

不一定,因为容器的普通迭代器最起码要支持++,--接口(比如:foward_list就不支持--,所以其没有反向迭代器)

这里补充一些关于[STL]文档的使用,从迭代器功能角度分为三类:

1. forward_iterator  (单向迭代器)      支持——>  ++              比如: foward_list等等

2. bidirectional_iterator(双向迭代器)   ——>  ++  --          比如: list等

3. radom_access_iterator  (随机迭起器) ——>  ++ --  + -   比如:vector, deque等, 第三中迭代器继承1,2种

那意义又是什么??

意义:就是提示在使用迭代器时,接口会提示你合适的的迭代器类型。

 

 

 

结语

   本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力

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

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

相关文章

RobotFramework之接口自动化流程测试

Robot Framework之接口测试自动化&#xff08;数据准备、数据脚本实现、实现层和断言层、测试报告&#xff09; 脚本用例通用模板设计 单接口用例测试 数据准备&#xff0c;已经取出了该接口的所有正向和逆向接口测试用例&#xff0c;那现在如何把数据和用例结合起来&#xff0…

fastadmin 自定义弹窗大小,遮罩关闭弹窗问题

// 更改表格里面的默认编辑按钮弹窗table.on(post-body.bs.table,function(){// 这里就是数据渲染结束后的回调函数$(".btn-editone,.btn-add").data("area", [80%,90%]);$(".btn-editone,.btn-add").data("shade", [0.6,"#000&q…

测试经理应该怎么写测试部门年终总结报告?

年终总结一般对季度、半年度或年度总结的一个整理&#xff0c;我们需要定期对工作中的内容进行定期总结和复盘。将每一次复盘中总结出来的一些收获叠加起来&#xff0c;在针对性地调整一下&#xff0c;就是一份合格的年终总结。具体可以分为如下几个步骤&#xff1a; 1.先把这…

【第一阶段】kotlin语言的Nothing类型

fun main() {show(60) } //两种写法一样 private fun show(num:Int){when(num){//下面这句话不是注释提示&#xff0c;会终止程序-1->TODO("不符合")in 0..59->println("不及格")in 60..89->println("及格")in 90..100->println(&qu…

Zookeeper特性与节点数据类型详解

CAP&Base理论 CAP理论 cap理论是指对于一个分布式计算系统来说&#xff0c;不可能满足以下三点: 一致性 &#xff1a; 在分布式环境中&#xff0c;一致性是指数据在多个副本之间是否能够保持一致的 特性&#xff0c;等同于所有节点访问同一份最新的数据副本。在一致性的需…

PPT颜色又丑又乱怎么办?

一、设计一套PPT时&#xff0c;可以从这5个方面进行设计 二、PPT颜色 &#xff08;一&#xff09;、PPT常用颜色分类 一个ppt需要主色、辅助色、字体色、背景色即可。 &#xff08;二&#xff09;、搭建PPT色彩系统 设计ppt时&#xff0c;根据如下几个步骤&#xff0c;依次选…

时间复杂度空间复杂度相关练习题

1.消失的数字 【题目】&#xff1a;题目链接 思路1&#xff1a;排序——》qsort快排——》时间复杂度O&#xff08;n*log2n&#xff09; 不符合要求 思路2&#xff1a;&#xff08;0123...n)-(a[0]a[1][2]...a[n-2]) ——》 时间复杂度O&#xff08;N&#xff09;空间复杂度…

Leetcode-每日一题【剑指 Offer 15. 二进制中1的个数】

题目 编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位数为 1 的个数&#xff08;也被称为 汉明重量).&#xff09;。 提示&#xff1a; 请注意&#xff0c;在某些语言&#xff08;如 Java&…

计算机网络 ARP协议 IP地址简述

ARP只能在一个链路或一段网络上使用

气体检测仪语音报警芯片,可自行烧录的音频芯片,WT588F02B-8S

近年来&#xff0c;安全问题备受关注&#xff0c;特别是涉及气体泄漏的危险场景。 为了进一步增强气体检测仪的安全功能&#xff0c;市面上便研发出了一款有害气体报警器&#xff0c;并采用WT588F02B-8S语音提示芯片为元器件&#xff0c;为产品赋予更多声音&#xff0c;更多警示…

嵌入式开发学习(STC51-2-创建工程模板)

创建工程模板&#xff08;以多文件模板为例&#xff09; 打开keil&#xff0c;创建新工程 选择文件目录&#xff0c;起名 选择芯片类型 &#xff08;没有找到对应芯片类型的可以网上搜索&#xff1a;keil配置stc系列器件支持包&#xff0c;配置好后就可以了&#xff09; 创…

泰国的区块链和NFT市场调研

泰国的区块链和NFT市场调研 基本介绍 参考&#xff1a; https://zh.wikipedia.org/zh-hans/%E6%B3%B0%E5%9B%BD参考&#xff1a; https://hktdc.infogram.com/thsc–1h7k2303zo75v2x zz制度&#xff1a; 君主立宪制&#xff08;议会制&#xff09; 国王&#xff1a; 玛哈哇集拉…

hbuilder的获取头像以及位置

条件编译 // #ifndef VUE3 import Vue from vue import ./uni.promisify.adaptor Vue.config.productionTip false App.mpType app // 初始化vue应用 const app new Vue({...App }) // 挂载vue应用 app.$mount() // #endif// #ifdef VUE3 import { createSSRApp } from vue…

Pet Detection System (PDS)

宠物医院检验设备物联系统

PHP原生类

什么是php原生类 原生类就是php内置类&#xff0c;不用定义php自带的类&#xff0c;即不需要在当前脚本写出&#xff0c;但也可以实例化的类 我们可以通过脚本找一下php原生类 <?php $classes get_declared_classes(); foreach ($classes as $class) {$methods get_clas…

c++中const修饰成员函数的问题

问题引入&#xff1a; 看下面这一段代码&#xff1a; class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;}void Print(){cout << "Print()" << endl;cout << "year:" << _year <&…

利用hfish反控境外攻击源主机

导师给了7个网络安全课题选题&#xff0c;本想和他聊了下思路&#xff0c;他一挥手让我先做出点东西再来聊就把我打发走了…… 正好前段时间阿里云到校做推广&#xff0c;用优惠卷薅了一台云服务器&#xff0c;装了hfish先看下情况 没想到才装上没两天数据库就爆了&#xff0…

CDN安全面临的问题及防御架构

CDN安全 SQL注入攻击&#xff08;各开发小组针对密码和权限的管理&#xff0c;和云安全部门的漏洞扫描和渗透测试&#xff09; Web Server的安全&#xff08;运营商和云安全部门或者漏洞纰漏第三方定期发布漏洞报告修复&#xff0c;例如&#xff1a;nginx版本号和nginx resol…

MuMu模拟器运行一段时间后Device.Present耗时突然上升

1&#xff09;MuMu模拟器运行一段时间后Device.Present耗时突然上升 2&#xff09;​如何在运行过程中获得温度信息 3&#xff09;Input System鼠标更换主按键的Bug 4&#xff09;如何禁止Unity向https://config.uca.cloud.unity3d.com发送设备信息 这是第347篇UWA技术知识分享…

手把手写教学C#写一个串口接收助手

C#上位机 文章目录 C#上位机[TOC](文章目录) C#一个简单串口助手创建工程&#xff1a;一、控件的设置1.显示TextBOX2.选项框comboBOX3.标签label4.按键button 二、组件的设置1.添加定时器time组件2.添加串口组件serialPort 三、代码部分总结 C#一个简单串口助手 实现简单的串口…