【C++】优先级队列 priority_queue,仿函数和函数对象,反向迭代器

news2024/9/22 5:42:18

目录

1.介绍和实现

2.仿函数和函数对象

3.oj

4.反向迭代器


1.介绍和实现

他也在<queue>的头文件里

但是他的底层是堆,并不满足先入先出(不要一看到queue就先入先出)

 

他也是一个容器适配器,用vector适配的,为什么?因为他底层是一个堆,之前就说过堆这种结构就是一个顺序表

那么这个实现岂不是爽歪歪, 堆的复习在这里

直接写了,没什么新东西

namespace wrt
{
	template<class T,class Container=vector<T>>
	class priority_queue
	{
	public:
		priority_queue()
		{}
		template <class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
			:_con(first,last)
		{
			for (size_t i= (_con.size() - 1 - 1 )/ 2; i >=0; i--)//一个节点-1  /2  是用来算父亲节点的 
			{
				//向下调整
				adjust_down(i);
			}
		}
		void adjust_up(size_t child)  //违背祖宗...
		{
			size_t  parent = (child - 1) / 2;
			while (child >0)
			{
				if (_con[child] > _con[parent])
				{
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}
		void adjust_down(size_t parent)
		{
			size_t  child = 2 * parent + 1;//假设是左孩子,并且左孩子是最大的孩子
			while (child < _con.size())
			{
				if (child + 1 < _con.size()&&_con[child + 1] > _con[child]  )
				{
					child ++;
				}
				//此时的child已经是最大的孩子
				if (_con[parent] < _con[child])
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = 2 * parent + 1;
				}
				else
				{
					break;
				}
			}
		}
		void push(const T& x)//插入数据就是向上调整
		{
			_con.push_back(x);
			adjust_up(_con.size() - 1);
		}
		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			adjust_down(0);
		}
		const T& top() const
		{
			return _con[0];
		}
		bool empty() const 
		{
			return _con.empty();
		}
		size_t size()
		{
			return _con.size();
		}
	private:
		Container _con;
	};
	void test()
	{
		priority_queue<int> pq;
		pq.push(1);
		pq.push(4);
		pq.push(6);
		pq.push(7);
	
		while (!pq.empty())
		{
			cout << pq.top() << " ";
			pq.pop();
		}
		cout << endl;
	}
}

2.仿函数和函数对象

注意到这有一个类,其实仿函数也是一个类,仿函数类型的 对象 叫函数对象(因为他可以像函数一样使用)

namespace wrt
{
	template<class T>
	struct  less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};
	template<class T>
	struct greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};
}

这里的less类和greater类就是两个仿函数,

 

lessFunc是仿函数less实例化对象,根据前面的定义,他叫函数对象

实例化之后我们看lessFunc()这个形式就很像函数调用有木有,所以这就是函数对象的特征:像函数一样使用 

那么仿函数存在的意义是什么?

回顾之前C里学过的比较函数,qsort最经典的 最后一个参数需要比较函数

还有快排,冒泡....我们自己写过的函数

void BubbleSort(T* a, int n)
{
	for (int j = 0; j < n; ++j)
	{
		int exchange = 0;
		for (int i = 1; i < n - j; ++i)
		{
			if (a[i] < a[i - 1])
			{
				swap(a[i - 1], a[i]);
				exchange = 1;
			}
		}

		if (exchange == 0)
		{
			break;
		}
	}
}

 这样写就把函数写死了

但用一下仿函数

void BubbleSort(T* a, int n, Compare com)
{
	for (int j = 0; j < n; ++j)
	{
		int exchange = 0;
		for (int i = 1; i < n - j; ++i)
		{
			//if (a[i] < a[i - 1])
			if (com(a[i], a[i - 1]))
			{
				swap(a[i - 1], a[i]);
				exchange = 1;
			}
		}

		if (exchange == 0)
		{
			break;
		}
	}
}

写成这样我们根本不知道这个函数是升序降序

但是根据我的要求,他是可控的

 

 

所以这就是升序 

想要降序?

 这个降序就是匿名函数实现的,如果我只有这一块需要用到greater仿函数类,那就匿名函数更省事,反之还是像less仿函数那么写就好了

这个地方com类型需要是const & 吗?其实没太大必要,要不然你上面的仿函数类的运算符()重载还得写成const的,因为这个com根本不大,仿函数里面没有成员变量,所以不就是1字节

我们平时加上const& 就是为了防止参数过大,可以不需要拷贝构造一次 


 那么我们把仿函数应用在刚才写的堆里面

向上调整的比较部分可以更改,一定要注意,库里面默认就是大堆用less

less是x < y ,所以要注意顺序com(_con[parent],_con[child])   ——com(x,y)

void adjust_up(size_t child)  //违背祖宗...
		{
			Compare com;
			size_t  parent = (child - 1) / 2;
			while (child >0)
			{
				if(com(_con[parent],_con[child]))
				//if (_con[child] > _con[parent])
				{
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

然后向下调整部分的比较也改一下

	void adjust_down(size_t parent)
		{
			Compare com;
			size_t  child = 2 * parent + 1;//假设是左孩子,并且左孩子是最大的孩子
			while (child < _con.size())
			{
			//	if (child + 1 < _con.size() && _con[child + 1] > _con[child])
			if (child + 1 < _con.size() && com( _con[child],_con[child + 1])
				{
					child++;
				}
				//此时的child已经是最大的孩子
				//if (_con[parent] < _con[child])
				if (com(_con[parent], _con[child]))
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = 2 * parent + 1;
				}
				else
				{
					break;
				}
			}
		}

运行一下发现刚刚好

 现在想实现小堆怎么搞?

实例化的时候不再使用缺省参数,缺省了就是堆的默认(大堆)

 变成这样


现在玩点花的,看一下之前写过的日期类,我们用仿函数比较日期

日期类

 

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)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}

private:
	int _year;
	int _month;
	int _day;
};

 发现很对

 但是如果T是日期类指针

怎么不对?小堆的堆顶不是最小的日期,这个函数首先肯定没写错,并且发现每次运行的结果居然都不一样,这时候仿函数比较的就是地址,每次new的地址都是随机的,所以不一定哪个日期的地址就大/小

这个不符合我们预期,怎么办?

自己写仿函数,因为刚才的less就是自己写的,所以再造一个很轻松

struct PDateLess
{
	bool operator()(const Date* d1, const Date* d2)
	{
		return *d1 < *d2;
	}
};

struct PDateGreater
{
	bool operator()(const Date* d1, const Date* d2)
	{
		return *d1 > *d2;
	}
};

 

这样就没问题啦,所以这也是泛型,之前的泛型都是数据类型的广泛支持,但是现在他一定程度上也影响了我们的逻辑

 但是现在的仿函数只是冰山一角,现在浅看一下,更复杂的我们以后慢慢学习


 3.oj

数组中第K大的数字

可以建小堆,或者大堆

建大堆,然后pop k-1次就可以取到第k大,这个是时间复杂度就是O(N+(k-1)*logN)

N个元素建堆是O(N),然后popk-1次就是 (k-1)*logN

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
priority_queue<int>pq(nums.begin(),nums.end()) ;
while(--k)
{
    pq.pop();
}
return pq.top();
    }
};

 还可以建k个数字的小堆,堆顶就是k个数中最小的,每次有更大的就进堆,然后向下调整

最后堆顶就是第k最大,时间复杂度O(k+(n-k)*logn)

k个数字建小堆,然后剩下的最坏情况每一次比较之后都可以入,就是(n-k)*logn

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
priority_queue<int,vector<int>,greater<int>> pq(nums.begin(),nums.begin()+k) ;
for(size_t i=k;i<nums.size();i++)
{
   if(nums[i]>pq.top())
   {
       pq.pop();
   pq.push(nums[i]);

   }
}
return pq.top();
    }
};


4.反向迭代器

 

泛型思维去理解,这个反向迭代器肯定是用正向迭代器封装的

template<class Iterator>
class ReverseIterator
{
	typedef ReverseIterator<Iterator> Self;

public:
	ReverseIterator(Iterator it)
		:_it(it)
	{}


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

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

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

private:
	Iterator _it;
};

 上面的函数都很简单嘛,但是*运算符重载怎么写?

下面是反向迭代器的错误理解

 正确理解大佬的思路(对称美)

正常解引用就很坑 ,需要返回前一个位置,但是我不知道数据类型,库里面的方式就是萃取,绕了很多层,但是我们还可以用之前 那个多传几个模板的思路

template<class Iterator, class Ref, class Ptr> 
class ReverseIterator
{
	typedef ReverseIterator<Iterator, Ref, Ptr> Self;

public:
	ReverseIterator(Iterator it)
		:_it(it)
	{}

	Ref operator*()
	{
		Iterator tmp = _it;
		return *(--tmp);
	}

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

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

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

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

private:
	Iterator _it;
};


然后list.cpp的源文件里这个反向迭代器.h一包含

这样就可以使用啦,当然在vector里面也可以同样方法使用 

 

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

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

相关文章

利用Qemu工具仿真ARM64平台

Windows系统利用Qemu仿真ARM64平台0 写在最前1 Windows安装Qemu1.1 下载Qemu1.2 安装Qemu1.3 添加环境变量1.4测试安装是否成功2. Qemu安装Ubuntu-Server-Arm-642.1 安装前的准备2.2 安装Ubuntu server arm 64位镜像3 Windows配置Qemu网络和传输文件3.1 参考内容3.2 Windows安装…

数据湖架构Hudi(五)Hudi集成Flink案例详解

五、Hudi集成Flink案例详解 5.1 hudi集成flink flink的下载地址&#xff1a; https://archive.apache.org/dist/flink/ HudiSupported Flink version0.12.x1.15.x、1.14.x、1.13.x0.11.x1.14.x、1.13.x0.10.x1.13.x0.9.01.12.2 将上述编译好的安装包拷贝到flink下的jars目录…

Python(青铜时代)——字符串

字符串的定义与操作 字符串就是 一串字符 &#xff0c;是编程语言中表示文本的数据类型 在Python中使用一对双引号 "" 或者一对单引号来定义. 使用索引获取一个字符串中 指定位置的字符&#xff0c;索引计数从0开始 可以用 for/while 循环遍历字符串中的每一个字符…

NGINX学习笔记(一):一篇了解NGINX的基本概念

NGINX是什么&#xff1f; NGINX是一款由俄罗斯人伊戈尔赛索耶夫使用C语言开发的、支持热部署的、轻量级的WEB服务器/反向代理服务器/电子邮件代理服务器&#xff0c;因为占用内存较少&#xff0c;启动极快&#xff0c;高并发能力强&#xff0c;所以在互联网项目中广泛应用。可…

CRM系统的四种数据分析法

在数字化时代&#xff0c;数据就是一切。因此&#xff0c;通过数据来支撑企业决策&#xff0c;才能确保制定的决策在更大程度上保持准确。因此&#xff0c;CRM客户管理系统的数据分析能力不容忽略。CRM获取的客户信息&#xff0c;就是很好的数据支撑样本&#xff0c;让企业从数…

CANfd 一次采样点和二次采样点

CANfd 一次采样点和二次采样点 采样点的定义 采样点是CAN控制器读取总线电平&#xff0c;并解释各个比特的逻辑值的时间点。 首先我们需要了解Tq的概念&#xff0c;Tq是can控制器的最下时间周期称作时间份额&#xff08;Time quantum&#xff0c;简称Tq&#xff09;,它是通过芯…

2023年3月全国DAMA-CDGA/CDGP数据治理认证招生简章

弘博创新是DAMA中国授权的数据治理人才培养基地&#xff0c;贴合市场需求定制教学体系&#xff0c;采用行业资深名师授课&#xff0c;理论与实践案例相结合&#xff0c;快速全面提升个人/企业数据治理专业知识与实践经验&#xff0c;通过考试还能获得数据专业领域证书。 DAMA认…

嵌入式学习笔记——认识STM32的 GPIO口

寄存器开发STM32GPIO口前言认识GPIOGPIO是什么GPIO有什么用GPIO怎么用STM32上GPIO的命名以及数量GPIO口的框图&#xff08;重点&#xff09;输入框图解析三种输入模式GPIO输入时内部器件及其作用1.保护二极管2.上下拉电阻&#xff08;可配置&#xff09;3.施密特触发器4.输入数…

Spark 存储系统

Spark 存储系统MemoryStoreDiskStoreSpark 存储系统架构&#xff1a; Spark 存储系统维护的数据 : Shuffle 中间文件 &#xff1a;Shuffle Map 输出数据 &#xff0c; 消耗节点磁盘广播变量 &#xff1a;在 Executors 内保存所有数据 &#xff0c;消耗节点的内存RDD Cache : 将…

大数据技术之Hive(五)拉链表的设计与实现

一、什么是拉链表针对订单表、订单商品表&#xff0c;流水表&#xff0c;这些表中的数据是比较多的&#xff0c;如果使用全量的方式&#xff0c;会造成大量的数据冗余&#xff0c;浪费磁盘空间。所以这种表&#xff0c;一般使用增量的方式&#xff0c;每日采集新增的数据。在这…

DevOps平台之GitLab 账户个性化设置【二】

1、简介 上一篇文章安装完GITLAB服务之后&#xff0c;我们可以很方便地从浏览器登录上去进行仓库项目管理。 但是初始化的界面默认设备并不是能很好地使用&#xff0c;比如语言&#xff0c;皮肤&#xff0c;个人信息不完善等等。 所以本文就为了完善这些。 上一篇&#xff1a;…

Linux 配置本地yum源

挂载光盘 进入包 配置路径&#xff0c;查看在线yum源 移动在线yum源到/home/目录下 进入vi,任意取名以.repo结尾即可 按住i进行编辑&#xff0c;输入以下内容 注意gpgcheck1是检验&#xff0c;配置本地yum源不需要检验 写入上图内容按住&#xff1a;输入wq&#xff0c;点击回车…

LeetCode - 1653 使字符串平衡的最少删除次数

目录 题目来源 题目描述 示例 提示 题目解析 算法源码 题目来源 1653. 使字符串平衡的最少删除次数 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个字符串 s &#xff0c;它仅包含字符 a 和 b​​​​ 。 你可以删除 s 中任意数目的字符&#xff0c;使得 …

广州银行冲刺A股上市:不良贷款规模突破100亿元,不良率飙升

又一家城商行平移申报IPO。近日&#xff0c;广州银行股份有限公司&#xff08;下称“广州银行”&#xff09;递交招股书&#xff0c;准备在深圳证券交易所主板上市。本次冲刺上市&#xff0c;广州银行计划募资约94.79亿元&#xff0c;国泰君安证券为其保荐机构。 截至目前&…

省选模拟测试23 T1直径

题目大意 给你一个数kkk&#xff0c;请你构造一棵节点数量小于等于5000的直径数量为kkk的树。 我们定义这棵树的直径为&#xff0c;所有满足1≤i<j≤n1\leq i<j\leq n1≤i<j≤n的(i,j)(i,j)(i,j)中&#xff0c;dis(i,j)dis(i,j)dis(i,j)最大的。如果有多个这样的(i,…

buu RSA what 1

题目描述&#xff1a; 题目四个文件&#xff0c;分别如下&#xff1a; rsa.py from Crypto.Util.number import bytes_to_long, getPrime from random import randint from gmpy2 import powmodp getPrime(2048) q getPrime(2048) N p*q Phi (p-1)*(q-1) def get_enc_key…

珠海先达盈致数据智能监控器+SaaS平台 轻松实现注塑生产管控

数据智能监控器 兼容市面上99%的注塑设备 直接读取设备生产数据与状态&#xff0c;如&#xff1a;计划产出、实际产出、累计产出、停机、节拍、线利用率、直通率、停产时间、工单状态、OEE、注胶时间…… 产品功能价值 ◎ OEE不达标报警&#xff0c;一手掌握生产效能 ◎ 首…

论文精读:Ansor: Generating High-Performance Tensor Programs for Deep Learning

文章目录1. Abstract2. Introduction3. Background4. Design Overview5. Program Sampling5.1 Sketch Generation5.2 Random Annotation6. Performance Fine-tuning6.1 Evolutionary Search6.2 Learned Cost Model7. Task Scheduler7.1 Problem Formulation7.2 Optimizing with…

使用nvm管理node版本

下载nvm版本参考文章&#xff1a;https://blog.csdn.net/langmanboy/article/details/126357952下载安装选择nvm的目录为d:\nvm&#xff0c;nodejs的目录为d:\nodejs&#xff1a;v14.0.0&#xff1a;执行nvm install 14生成的目录v16.0.0&#xff1a;执行nvm install 16生成的目…

详解vite原理

背景 自从尤大大的 vite 问世后&#xff0c;现在前端的构建工具由 webpack 转向 vite 的越来越多&#xff0c;今天主要来讨论一下 vite 的一些工作原理&#xff0c;讨论之前大家可以看这篇文章&#xff0c;尤其是注意其中谈到的 Bundleless&#xff0c;这样也能更好的理解 vit…