【C++】仿函数优先级队列反向迭代器

目录

一、优先级队列

1、priority_queue 的介绍

2、priority_queue 的使用

3、 priority_queue 的模拟实现

1)priority_queue()/priority_queue(first, last)

2)push(x)

3)pop()

4) top()

5) empty ()

​6)size ()

二、仿函数

1、定义

三、完整代码

四、反向迭代器

1、重新定义一个类

2、复用正向迭代器

3、完整代码


一、优先级队列

1、priority_queue 的介绍

⭕ 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。

⭕ 此上下文类似于,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。

⭕优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。

⭕底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:

empty():检测容器是否为空

size():返回容器中有效元素个数

front():返回容器中第一个元素的引用

push_back():在容器尾部插入元素

pop_back():删除容器尾部元素

⭕标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue

⭕ 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数 make_heap、push_heap和pop_heap来自动完成此操作。

【优先级队列的官方文档】

2、priority_queue 的使用

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

1)priority_queue()/priority_queue(first, last)

功能:构造一个空的优先级队列。

2)empty()

功能:检测优先级队列是否为空,是返回true,否则返回 false

3)top ()

功能:返回优先级队列最大(或最小)元素,即堆顶元素

4)push(x)

功能:在优先级队列中插入元素x

5)pop()

功能:删除优先级队列最大(或最小)元素,即堆顶元素

如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供 > 或者< 的重载。

3、 priority_queue 的模拟实现

通过对 priority_queue 的底层结构就是堆,因此此处只需对对进行通用的封装即可。其底层本质是一个二叉树的堆,使用 vector 来构建,再加上堆的算法,将这个线性表构建成堆的结构。

1)priority_queue()/priority_queue(first, last)

优先级队列实例化出的对象本身就是一个堆,所以我们需要在它的构造函数里就将建堆工作做好。不像前面的stack和queue我们不需要写构造函数的,因为自定义成员变量,初始化时,会调用默认构造函数或者它自己的构造函数。而这里构造函数需要构造出一个堆,需要我们自己手动操作了。

优先级队列可以使用迭代器来初始化:

2)push(x)

先插入一个数据到数组里,但是需要保留堆的结构,我们可以使用向上调整法。

3)pop()

 删除堆顶元素,直接删除会破坏堆的结构。可以将堆顶元素和最后一个元素相交换,删除最后一个元素,将堆顶元素向下调整。

4) top()

返回堆顶元素。直接返回 _con 下标为0的值。

5) empty ()

检查优先级队列是否为空,直接判断 _con 是否为空即可。

 6)size ()

返回优先级队列的个数,返回 _con 的个数即可

二、仿函数

1、定义

仿函数(functor),就是使一个类或者结构体的使用看上去像一个函数。其实现就是类中重载 operator() 运算符,这个类就有了类似函数的行为,类的对象可以像函数一样使用。

我们在模拟实现优先级队列时会发现,我们只模拟了默认大堆的实现方式,如果要模拟实现小堆,难道要再重新写一份相同的代码吗?我们可以使用仿函数控制比较,进而控制大堆还是小堆,再增加一个模板参数用来传递仿函数,仿函数可以控制比较方式。这样就可以灵活的传递仿函数来控制创建大堆还是小堆。

三、完整代码

namespace zhou
{
	template<class T>
	class Less
	{
	public:

		bool operator()(T& x, T& y)//重载()运算符  
		{
			return x < y;
		}
	};

	template<class T>
	class Greater
	{
	public:

		bool operator()(T& x, T& y)//重载() 
		{
			return x > y;
		}
	};

	template<class T, class Container = vector<T>, class Comapre = less<T>>
	class priority_queue
	{
	 public:
		 

		void Adjustdown(int parent)
		{
			size_t child = 2 * parent + 1;
			while (child < _con.size())
			{
				//if (child + 1 < _con.size() && _con[child] < _con[child + 1)
				if (child + 1 < _con.size()
					&& com(_con[child], _con[child + 1]))
				{
					child++;
				}
				if (_con[parent] < _con[child])
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = 2 * parent + 1;
				}
				else
				{
					break;
				}
			}
		}
		template<class InputIterator>
		priority_queue(InputIterator begin, InputIterator last)
		{
			//第一首先将数据插入进去
			while (begin != last)
			{
				_con.push_back(*begin);
				++begin;
			}
			//第二需要建堆,默认建的是大堆--利用向下调整算法建堆
			//从最后一个叶子结点的父亲开始向下调整,然后依次往前走,直到走到堆顶。
			for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--)
			{
				Adjustdown(i);
			}
		}

		void Adjustup(int child)
		{
			Comapre com;
			size_t parent = (child - 1) / 2;
			while (child < _con.size())
			{
				//if (_con[child] > _con[parent])
				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);
			Adjustup(_con.size() - 1);
		}

		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			Adjustdown(0);
		}

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

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

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

	 private:
		Container _con;
	};

} 

四、反向迭代器

1、重新定义一个类

在 list 模拟实现的时候,只模拟实现了正向迭代器,反向迭代器还没有实现。接下来我们就可以来实现反向迭代器。之前我们是封装了一个类来实现正向迭代器,现在我们也同样封装一个反向迭代器的类。重新定义 rbegin() 和 rend().所以需要修改的是“++”和“--”的运算符重载。

 

【测验代码】

2、复用正向迭代器

⭕写一个反向迭代器固然可以实现目标的,但我们看 list 源代码时会发现他是对正向迭代器的复用。

⭕STL大佬在设计反向迭代器时,为了追求与正向迭代器的对称,将首尾指针得到指向反向保持一致,使rbegin()end()位置,rend()begin()位置。

⭕在这样的设计下,rbegin()和 rend()的实现就可以直接对应复用了,而 operator*()返回的就不是当前所指向的对象,而是成了上一个对象。

⭕前面在模拟实现list时,运用了多参数模板来解决const对象代码冗余问题,在反向迭代器的实现中也运用了相同原理,通过对形参传递不同的对象,变换为不同的迭代器,其中Ref表示引用对象,Ptr表示指针对象。

 

3、完整代码

//反向迭代器模拟实现
namespace zhou
{
	template<class Iterator, class Ref, class Ptr>
	struct ReverseIterator
	{
		typedef ReverseIterator<Iterator, Ref, Ptr> Self;
		Iterator _cur;

		//用正向迭代器来构造反向迭代器
		ReverseIterator(Iterator it)
			:_cur(it)
		{}

		Ref operator*()
		{
			//为了对称返回前一个位置
			Iterator tmp = _cur;
			--tmp;
			return *tmp;
		}

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

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

		bool operator!=(const Self& s)
		{
			return _cur != s._cur;
		}
	};
}

//完整 list 模拟实现
namespace zhou
{
	template<class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _data;

		list_node(const T& x = T())
			:_next(nullptr)
			, _prev(nullptr)
			, _data(x)
		{}
	};

	template<class T, class Ref, class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> node;
		typedef __list_iterator<T, Ref, Ptr> self;
		node* _node;

		__list_iterator(node* n)
			:_node(n)
		{}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &_node->_data;
		}

		self& operator++()
		{
			_node = _node->_next;

			return *this;
		}

		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		self& operator--()
		{
			_node = _node->_prev;

			return *this;
		}

		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;

			return tmp;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}
	};

	//定义一个反向迭代器的类
	/*template<class T, class Ref, class Ptr>
	struct __list_reverse_iterator
	{
		typedef list_node<T> node;
		typedef __list_reverse_iterator<T, Ref, Ptr> self;
		node* _node;

		__list_reverse_iterator(node* n)
			:_node(n)
		{}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &_node->_data;
		}

		self& operator++()
		{
			_node = _node->_prev;

			return *this;
		}

		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_prev;

			return tmp;
		}

		self& operator--()
		{
			_node = _node->_next;

			return *this;
		}

		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}
	};*/

	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 __list_reverse_iterator<T, T&, T*> reverse_iterator;
		typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
		typedef ReverseIterator<iterator, const T&, const T*> const_reverse_iterator;
		
		reverse_iterator rbegin()
		{
			return reverse_iterator(end());
		}

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

		/*reverse_iterator rbegin()
		{
			return reverse_iterator(_head->_prev);
		}

		reverse_iterator rend()
		{
			return reverse_iterator(_head);
		}*/

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

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

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

		const_iterator end() const
		{
			//iterator it(_head->_next);
			//return it;
			return const_iterator(_head);
		}

		void empty_init()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}

		list()
		{
			empty_init();
		}

		template <class Iterator>
		list(Iterator first, Iterator last)
		{
			empty_init();

			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}


		void swap(list<T>& tmp)
		{
			std::swap(_head, tmp._head);
		}

		list(const list<T>& lt)
		{
			empty_init();

			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);
		}

		// lt1 = lt3
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				//it = erase(it);
				erase(it++);
			}
		}

		void push_back(const T& x)
		{

			insert(end(), x);
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_back()
		{
			erase(--end());
		}

		void pop_front()
		{
			erase(begin());
		}

		void insert(iterator pos, const T& x)
		{
			node* cur = pos._node;
			node* prev = cur->_prev;

			node* new_node = new node(x);

			prev->_next = new_node;
			new_node->_prev = prev;
			new_node->_next = cur;
			cur->_prev = new_node;
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());

			node* prev = pos._node->_prev;
			node* next = pos._node->_next;

			prev->_next = next;
			next->_prev = prev;
			delete pos._node;

			return iterator(next);
		}
	private:
		node* _head;
	};

	void list_list1()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);
		for (auto e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		list<int>::reverse_iterator rit = lt.rbegin();
		while (rit != lt.rend())
		{
			cout << *rit << " ";
			++rit;
		}
	}
}

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

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

相关文章

Datawhale 零基础入门数据挖掘-Task1 赛题理解

一、 赛题理解 Tip:此部分为零基础入门数据挖掘的 Task1 赛题理解 部分&#xff0c;为大家入门数据挖掘比赛提供一个基本的赛题入门讲解&#xff0c;欢迎后续大家多多交流。 赛题&#xff1a;零基础入门数据挖掘 - 二手车交易价格预测 地址&#xff1a;零基础入门数据挖掘 -…

【Vue】三、使用ElementUI实现图片上传

目录 一、前端代码实现 二、后端代码实现 三、调试效果实现 一、前端代码实现 废话不多说直接上代码 <el-form-item prop"image" label"上传图片" v-model"form.image"><el-upload:action"http://localhost:8…

关于vuex 的模块开发和使用

1、文件结构 2、modules 文件内容 例子&#xff1a; ccc.js 文件内容如下&#xff1a; // 基础配置项 const state {aa: [] }const mutations {setaa (state, data) {state.aa data} }const actions {} export default {namespaced: true, state,mutations,actions } **注…

【Linux】进程通信

目录 一、管道通信 二、共享内存 三、消息队列 一、管道通信 管道是由操作系统维护的一个文件&#xff0c;管道通信的本质就是将管道文件作为临界资源&#xff0c;实现不同进程之间的数据读写&#xff0c;但是管道只允许父子进程或者兄弟进程之间的通信。 管道文件本身是全…

Redis面试题以及答案

1. 什么是Redis&#xff1f;它主要用来什么的&#xff1f; Redis&#xff0c;英文全称是Remote Dictionary Server&#xff08;远程字典服务&#xff09;&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并…

虹科Pico汽车示波器 | 免拆诊断案例 | 2019 款东风悦达起亚K2车怠速起停系统工作异常

一、故障现象 一辆2019款东风悦达起亚K2车&#xff0c;搭载G4FG发动机&#xff0c;累计行驶里程约为9 400 km。车主反映&#xff0c;行驶至路口停车等红灯时&#xff0c;怠速起停&#xff08;ISG&#xff09;系统自动使发动机熄火&#xff0c;接着组合仪表提示“怠速起停已解除…

HarmonyOS/OpenHarmony应用开发-DevEco Studio 在MAC上启动报错

报错截图 报错详细内容 ------------------------------------- Translated Report (Full Report Below) -------------------------------------Process: devecostudio [8640] Path: /Applications/DevEco-Studio.app/Contents/MacOS/devecos…

【pip 安装pymssql报错】 Failed to build pymssql

在使用pip install pymssql安装pymssql时报如下图的错误&#xff1b; 报错截图 2&#xff09;查找资料说pip<3.0版本 &#xff0c;我也试了&#xff0c;不行。 你们也可以试一试&#xff1a;pip install"pymssql<3.0" 3&#xff09;我的成功方式&#xff1…

选择word中的表格VBA

打开开发工具 选择Visual Basic插入代码 Sub 选择word中的表格() Dim t As Table an MsgBox("即将选择选区内所有表格&#xff0c;若无选区&#xff0c;则选择全文表格。", vbYesNo, "提示") If an - 6 Then Exit Sub Set rg IIf(Selection.Type wdSel…

【C++】线程库

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;C 前言 C11标准引入了线程库&#xff0c;通过其可以在C程序中方便地创建和管理线程。以下是一些常用的线程库组件&#xff1a; std::thread&#xff1a;std…

【Linux第三课-基础开发工具的使用】yum、vim、gcc/g++编译器、gdb、Make/Makefile编写、进度条程序、git命令行简单操作

目录 yum - 软件包管理器快速认识yum快速使用yumyum搜索yum安装yum卸载 yum的周边 - yum的整个生态问题 vim快速介绍vimvim的模式命令模式插入模式低行模式 常见模式 -- 命令、低行命令模式 -- 光标的移动命令模式 -- 复制粘贴、剪贴、删除命令模式 -- 小写/大写替换模式命令模…

单片机-- 数电(3)

编码器与译码器 译码 &#xff1a;将二进制代码转化为其他进制的代码 编码 &#xff1a;就是将其他代码转换为二进制码 编码器的类型 1二进制编码器 用n位二进制数码对2的n次方个输入信号进行编码的电路 2二-十进制编码器 将0到9十个十进制数转化为二进制代码的电路 2…

PyTorch 深度学习(GPT 重译)(三)

六、使用神经网络拟合数据 本章内容包括 与线性模型相比&#xff0c;非线性激活函数是关键区别 使用 PyTorch 的nn模块 使用神经网络解决线性拟合问题 到目前为止&#xff0c;我们已经仔细研究了线性模型如何学习以及如何在 PyTorch 中实现这一点。我们专注于一个非常简单…

Python 解析CSV文件 使用Matplotlib绘图

数据存储在CSV文件中&#xff0c;使用Matplotlib实现数据可视化。 CSV文件&#xff1a;comma-separated values&#xff0c;是在文件中存储一系列以‘&#xff0c;’分隔的值。 例如&#xff1a;"0.0","2016-01-03","1","3","20…

性能测试-Jmeter中IF控制器使用

一、Jmeter控制器 分为两种类型&#xff1a; 控制测试计划执行过程中节点的逻辑执行顺序&#xff0c;如&#xff1a;循环控制器&#xff0c;if控制器等对测试计划中的脚本进行分组&#xff0c;方便Jmeter统计执行结果以及进行脚本的运行时控制等&#xff0c;如&#xff1a;吞…

【ann2coco】图像label转coco格式的JSON

目录 &#x1f34b;&#x1f34b;需求 &#x1f34b;&#x1f34b;coco格式 &#x1f34b;&#x1f34b;python代码实现 整理不易&#xff0c;欢迎一键三连&#xff01;&#xff01;&#xff01; 送你们一条美丽的--分割线-- &#x1f34b;&#x1f34b;需求 单波段灰度图l…

7-6 混合类型数据格式化输入

题目链接&#xff1a;7-6 混合类型数据格式化输入 一. 题目 1. 题目 2. 输入输出格式 3. 输入输出样例 4. 限制 二、代码 1. 代码实现 #include <stdio.h>int main(void) {int num;char c;float f1,f2;if (scanf("%f %d %c %f", &f1, &num, &c…

【NLP笔记】预训练+微调范式之OpenAI Transformer、ELMo、ULM-FiT、Bert..

文章目录 OpenAI TransformerELMoULM-FiTBert基础结构Embedding预训练&微调 【原文链接】&#xff1a; BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding 【本文参考链接】 The Illustrated BERT, ELMo, and co. (How NLP Cracked Tra…

单片机LED灯闪烁

延时函数计算&#xff08;相关代码生成&#xff09;&#xff1a; #include "reg52.h" #include <INTRINS.H> void Delay500ms() //11.0592MHz {unsigned char i, j, k;_nop_();_nop_();i 22;j 3;k 227;do{do{while (--k);} while (--j);} while (--i); }vo…

以太网协议(数据链路层)

一些基础知识: 平时使用的网线,也叫做"以太网线".平时使用的交换机,也叫做"以太网交换机".以太网这个协议,既涉及到数据链路层的内容,也涉及到物理层的内容. 1. 以太网帧的格式 ①目的地址,源地址: 此处的地址,叫做mac地址(物理地址).作用也是区分不同…
最新文章