【C++】仿函数和priority_queue(优先级队列)

news2024/11/30 10:49:47

目录

一、仿函数 

二、priority_queue(优先级队列)

1、概念:

2、使用:

3、数组中第K个最大元素

4、priority_queue的模拟实现


一、仿函数 

①、概念:

仿函数,即函数对象。一种行为类似函数的对象,调用者可以像函数一样使用该对象,其实现起来也比较简单:用户只需实现一种新类型,在类中重载operator()即可,参数根据用户所要进行的操作选择匹配。

②、代码:

  •  用内置类型比较大小关系:
//仿函数/函数对象 --- 对象可以像调用函数一样去使用
struct less
{
	//()运算符重载--用于比较大小
	bool operator()(int x, int y)
	{
		return x < y;
	}
};
  • 利用模板比较less: 
template<class T>
struct less//用于 < 的比较
{
	bool operator()(const T& x, const T& y) const
	{
		return x < y;
	}
};
  • 利用模板比较greater
template<class T>
struct greater//用于  > 的比较
{
	bool operator()(const T& x, const T& y) const
	{
		return x > y;
	}
};

 ③、测试: 

//测试less
less<int> LessFunc;
cout << LessFunc(1, 2) << endl;//1
//测试greater
greater<int> GreaterFunc;
cout << GreaterFunc(1, 5) << endl;//0

为什么说仿函数又叫函数对象?

比如测试代码中的LessFunc,它是个对象,但他调用时直接写为LessFunc(1, 2),就像一个函数在调用,但他并不是函数,调用的本质是lessFunc.operator()(1, 2),所以仿函数即对象可以像函数一样使用

④、algorithm中的sort

 sort的第三个参数使用到了仿函数,其为仿函数的对象

第一个参数和第二个参数都是迭代器,第三个参数是仿函数,其默认值为less

升序:less <       降序:greater >  

注:第三个参数不传默认是less,即排升序

#include<iostream>
#include<algorithm>
using namespace std;

void test_sort()
{
	vector<int>v;
	v.push_back(1);
	v.push_back(4);
	v.push_back(5);
	v.push_back(2);
	//升序,less <
	sort(v.begin(), v.end());

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	//降序 greater >
	// 写法一、定义一个对象 
	//greater<int> gt;
	//sort(v.begin(), v.end(),gt);
	//写法二、匿名对象(更推荐)
	sort(v.begin(), v.end(), greater<int>());//greater<int>()是匿名对象
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

}

int main()
{
	//test_priority_queue();
	test_sort();

	return 0;
}


二、priority_queue(优先级队列)

1、概念:

 优先级队列queue不一样,它是优先级高的先走(默认情况是大的数优先级高,但如果想要小的优先级高,如何操作?-> 用仿函数),它的底层其实是中的大堆,不用数组的原因是堆的效率高

2、使用:

 queue的头文件同时包含了priority_queue和queue,所以用priority_queue直接#include<queue>即可,

注:容器适配器都不支持迭代器遍历,因为他们通常都包含一些特殊性质,如果支持迭代器随便遍历,那他们无法很好的保持他的性质,这里priority_queue也是容器适配器,故不支持迭代器

由运行结果可知,默认情况下是大的数优先级高,若要使小的优先级高,需要调用仿函数,priority_queue的第一个参数是值的类型,第二个参数是内部的适配容器,第三个参数是仿函数,而仿函数要引头文件 #include<functional>(仿函数下文会讲)

下面给出使小的优先级高的代码:

3、数组中第K个最大元素

基本介绍后我们来做一道可用priority_queue实现的题(三种解法)

 法一、优先级队列实现

class Solution {
public:
	int findKthLargest(vector<int>& nums, int k) {
		priority_queue<int> pq;
		for (auto e : nums)
			pq.push(e);//把所有数据插入到pq中

		while (--k)
			pq.pop();//执行了k-1次的删除

		return pq.top();//剩下的一个元素就是第k大的
	}
};

时间复杂度:O(N*logN)

解释:优先级队列建堆:O(N),插入数据push:O(N*logN),删除数据pop:O(k*logN),三者相加,综上时间复杂度为O(N*logN)

空间复杂度:O(N)

因为开辟的优先级队列的空间,故为O(N)

法二、用算法中的sort实现

class Solution {
public:
	int findKthLargest(vector<int>& nums, int k) {
		//解法二:
		sort(nums.begin(), nums.end());//不用仿函数的话默认情况下排升序
		return nums[nums.size() - k];//返回倒数第二个即可
	}
};

时间复杂度:O(N*logN)

因为sort的底层是快排,快排的时间复杂度:O(N*logN)

空间复杂度:O(1)

现假设N是一千万,K是100,这时时间空间消耗都很大,怎么优化?建堆实现

法三、用堆实现(类似于TopK问题)

这个其实跟TopK问题差不多,只不过这里是找第k个大的那个,故建有k个数的小堆(那么这k个数最后都会变成前k大的数),只要比堆顶大,就能进入堆中

下面是我之前写过的TopK求解思路(推荐看,便于理解):

【数据结构】---TopK问题_姜暮、的博客-CSDN博客

class Solution {
public:
	int findKthLargest(vector<int>& nums, int k) {
		//解法三:
		//利用仿函数建小堆,因为默认情况下优先级队列是建大堆
		priority_queue<int, vector<int>, greater<int>>minHeap;
		size_t i = 0;
		for (; i < k; ++i)
			minHeap.push(nums[i]);//先入k个数据(数据是什么无所谓)

        //使堆中是前k大的数据
		for (; i < nums.size(); ++i)
		{
			if (nums[i] > minHeap.top())
			{//比堆顶大的就替换堆顶
				minHeap.pop();
				minHeap.push(nums[i]);
			}
		}

		return minHeap.top();//因为是小堆,则堆顶即k个数中它是最小的,因为k个数是前k大的
	}
};

时间复杂度:O(N*logK)

解释:优先级队列建堆:O(N),k个数据的push:O(logk),因为本质是向上调整法≈k次,比较的过程:O(N*2*logk)三者相加,综上为O(N*logk) 

空间复杂度:O(K)

当N很大的时候,这个方法的效率是非常好的

4、priority_queue的模拟实现

向上调整法和向下调整法为什么需要用到仿函数?

因为堆分大堆小堆,向上和向下调整法对于大小堆的实现只有一个符号的差别,难道写两份调整法?不够好,故用仿函数实现,使大堆和小堆都能用一份代码

若参数是less,会建大堆,排升序
若参数是greater,会建小堆,排降序

为什么无需数组建堆?

之前讲的是已经存在数据的数组,他现有的顺序很可能不符合大堆或小堆的性质,故要数组建堆,这样插入删除等操作才能很好进行,而这里的模拟实现,一开始就没有数据,故不用建堆,它是每插入一个数据,调用向上调整法,删除数据,调用向下调整法,不断地插入和删除,还能使其保持堆的性质 

 priority_queue.h:

#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
#include<queue>
#include<functional>
#include<algorithm>

namespace mz
{
	//仿函数/函数对象 --- 对象可以像调用函数一样去使用
	template<class T>
	struct less
	{
		//()运算符重载--用于比较大小
		bool operator()(const T& x, const T& y) const
		{
			return x < y;
		}
	};
	template<class T>
	struct greater
	{
		//()运算符重载--用于比较大小
		bool operator()(const T& x, const T& y) const
		{
			return x > y;
		}
	};

	//优先级队列
	template<class T, class Container = vector<T>, class Compare = less<T>>
	class priority_queue
	{
	public:
		//向上调整算法
		void AdjustUp(int child)
		{
			Compare com;//创建一个仿函数对象
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (com(_con[parent], _con[child]))//利用仿函数建大堆或小堆
				{
					swap(_con[parent], _con[child]);
					//更新child和parent
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					//此时不需要调整,直接break
					break;
				}
			}
		}
		//向下调整算法
		void AdjustDown(int root)
		{
			int parent = root;
			int child = parent * 2 + 1;
			Compare com;//仿函数
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && com(_con[child],_con[child + 1]))
				{//利用仿函数,区别大小堆向下调整法的不同之处
					child++;
				}
				if (com(_con[parent], _con[child]))
				{
					swap(_con[parent], _con[child]);
					//更新child和parent
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					//此时不需要调整,直接break
					break;
				}
			}
		}
		//插入数据
		void push(const T& x)
		{
			_con.push_back(x);
			//每插入一个数据,都要向上调整建堆
			AdjustUp((int)_con.size() - 1);
		}
		//删除数据
		void pop()
		{
			assert(!_con.empty());//删除的前提:不为空
			swap(_con[0], _con[_con.size() - 1]);//交换头尾数据
			_con.pop_back();//删除最后一个数据
			AdjustDown(0);//从根部向下调整建堆
		}
		//取堆顶数据
		const T& top()
		{
			return _con[0];
		}
		//获取size有效数据个数
		size_t size()
		{
			return _con.size();
		}
		//判空
		bool empty()
		{
			return _con.empty();
		}
	private:
		Container _con;
	};
}

 test.cpp:

using namespace std;
#include"priority_queue.h"


void test_priority_queue()
{
	//priority_queue<int>pq;//默认大的优先级高
	mz::priority_queue<int, vector<int>, greater<int>> pq;//变成小的优先级高
	pq.push(3);
	pq.push(1);
	pq.push(9);
	pq.push(4);
	pq.push(2);

	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;

}

int main()
{
	test_priority_queue();
	//test_sort();

	return 0;
}

运行结果:

 

 STL的大体总结:

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

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

相关文章

Android studio 快捷键

目录 Ctrl N 搜索指定的 Java 类Ctrl F 查找文本Alt Enter 修复代码错误Ctrl Alt L 格式化代码Ctrl D 复制当前行或选中的内容Ctrl W 逐渐增加当前选中的范围Ctrl Shift - 折叠所有代码Ctrl Shift 展开所有代码Ctrl B 查看定义Ctrl Alt B 查看实现Ctrl Alt O …

Java版的数据结构——栈和队列

目录 1. 栈&#xff08;Stack&#xff09; 1.1 概念 1.2 栈的使用 1.3 栈的模拟实现 1.4 栈的应用场景 1.4.1 改变元素的序列 1.4.2 将递归转化为循环 2. 队列&#xff08;Queue&#xff09; 2.1 概念 2.2 队列的使用 2.3 队列模拟实现 2.4 循环队列 3. 双端队列&…

IO流(IO Stream)

​ 一、概述 我们已经系统学习了File 类&#xff0c;并且已经知道 File 类的实例用于表示文件或目录的路径 名。 虽然我们可以通过 File 实例来访问文件或目录的元数据&#xff0c;甚至可以创建、删除文件或目 录&#xff0c;但是&#xff0c;我们却不能通过File实例来访问文…

第六章 图 七、最短路径(BFS算法、Dijkstra算法、Floyd算法)

目录 一、BFS算法&#xff08;单源最短路径&#xff09; &#xff08;1&#xff09;介绍&#xff1a; &#xff08;2&#xff09;例子&#xff1a; 二、Dijkstra算法&#xff08;单源最短路径&#xff09; &#xff08;1&#xff09;介绍&#xff1a; &#xff08;2&#…

obsstudio下载使用

官网 Open Broadcaster Software | OBS 介绍 OBS是Open Broadcaster Software的简称&#xff0c;是一款开源&#xff0c;用于视频录制以及直播串流的软件&#xff0c;它支持Windows、Mac以及Linux操作系统。OBS使用容易、操作简单&#xff0c;对于新手小白来说非常友好。如果…

集卡拖车运输最新政策调整来了_箱讯科技

国庆佳节马上就要到了&#xff0c;我们即将迎来一个重要的假期。 然而&#xff0c;对于许多进出口企业来说&#xff0c;国庆节并不仅仅意味着放松和庆祝&#xff0c;还需要提前做好出运准备。准时出运对于维护客户信任和业务运营至关重要。 伴随着国庆节的临近&#xff0c;运…

2000-2021年上市公司全要素生产率数据(LP法)(含原始数据、计算代码、计算结果)

2000-2021年上市公司全要素生产率数据&#xff08;LP法&#xff09;&#xff08;含原始数据、计算代码、计算结果&#xff09; 1、时间&#xff1a;2000-2021年 2、指标&#xff1a;股票代码、年份、证券代码、固定资产净额、营业总收入、营业收入、营业成本、销售费用、管理…

论文阅读之Learning and Generalization of Motor Skills by Learning from Demonstration

0、论文基本信息 DMP经典论文 论文题目&#xff1a;Learning and Generalization of Motor Skills by Learning from Demonstration 会议名称&#xff1a;2009 ICRA 论文作者&#xff1a;Peter Pastor, Heiko Hoffmann, Tamin Asfour and Stefan Schaal 作者简介&#xff1a;…

如何导出数据库数据字典

1、随便找一个工程项目&#xff0c;在项目build.gradle配置文件添加以下依赖 compile group: cn.smallbun.screw, name: screw-core, version: 1.0.52、刷新下载依赖 3、将以下代码拷贝到工程任意Java目录 ScrewDemo.java package com.xxx;import cn.smallbun.screw.core.C…

Manim 中文显示问题报错解决办法

使用Manim直接制作这种动画视频的话是不支持中文的,需要进行一些修改,效果如图所示。 但是修改的方案都太坑,经过自己不断的研究找到了一个比较简单实用的方法。 文章目录 源文件修改代码修改源文件修改 实用everuthing找到ctex_template.tex文件。 我这里的路径是D:\MyT…

定时器+BOM

9.定时器BOM 1.定时器 **概念:**重复执行一个函数 1.1setInterval() setInterval(“代码/函数”,时间,参数),返回定时器的序列号,默认从1开始 clearInterval(序列号)清除定时 <button class"start">开启定时器</button><button class"close…

Elasticsearch:为具有许多 and/or 高频术语的 top-k 查询带来加速

作者&#xff1a;Adrien Grand Disjunctive queries&#xff08;term_1 OR term_2 OR ... OR term_n&#xff09;非常常用&#xff0c;因此在提高查询评估效率方面它们受到了广泛关注。 Apache Lucene 对于评估 disjunctive queries 有两个主要优化&#xff1a;一方面用于详尽评…

Mac os 安装homebrew

Mac os 不会自带homebrew工具&#xff0c;而很多需要的工具需要用brew命令下载和安装&#xff0c;但是因为homebrew是国外网站&#xff0c;如果没有VPN&#xff0c;就无法下载。下面记录一下如何解决这个问题。 1、国内安装的命令如下&#xff1a; /bin/zsh -c "$(curl …

[管理与领导-89]:IT基层管理者 - 扩展技能 - 5 - 职场丛林法则 -3- 在丛林(公司->团队->个人)中定位自己

目录 前言&#xff1a; 一、了解公司市场 1.1 了解公司的途径 1.2 了解一个公司的哪些方面 1.3 商业的本质 1.4 公司的本质 1.5 公司的残酷性 1.6 公司与丛林的相似性 1.7 公司在市场中的定位涉及以下几个方面&#xff1a; 1.8 等价交换与剩余价值 在职场中的体现 二…

input输入事件

我要实现input输入框一边输入&#xff0c;一边在控制台输出结果 现有如下代码 <body><input type"text" onchange"myFunction()" /><script>function myFunction(){console.log(999)}</script> </body> 当敲下回车键后才会…

用python爬豆瓣电影《热烈》短评

一、爬虫对象-豆瓣电影短评 今天分享一期爬虫案例&#xff0c;爬取的目标是&#xff1a;豆瓣上任意一部电影的短评&#xff08;注意&#xff1a;是短评&#xff0c;不是影评&#xff01;&#xff09;&#xff0c;以《热烈》这部电影为例&#xff1a; ▲ 爬取目标 爬取以上6个…

阿里云-源码构建容器镜像

1、阿里云Code代码平台 1.1 创建访问令牌。 登录云效Codeup控制台&#xff0c;单击右上角个人设置。 在个人设置页面&#xff0c;单击左侧导航栏中的个人访问令牌。 单击创建访问令牌&#xff0c;设置配置项&#xff0c;然后单击立即创建。以下为创建访问令牌需要授予的最小权…

【C语言】【strcpy的使用和模拟实现】

1.strcpy的使用&#xff1a; char* strcpy(char* destination,const char* source)返回类型是字符指针&#xff0c;参数是接受方字符串的首地址和要拷贝的字符串的首地址 从接受地的‘\0’开始拷贝&#xff0c;会将源字符串中的’\0’也拷贝过来目标空间必须足够大&#xff0…

PASCAL VOC2012数据集以及制作自己的数据集

目录 VOC2012目录结构制作自己的数据集标注图片软件使用流程软件使用 VOC2012目录结构 制作自己的数据集 标注图片软件 github开源项目&#xff0c;形成的是xml文件格式 使用流程 软件使用

docker四种网络模式

文章目录 一.为什么要了解docker网络二.docker 网络理论三.docker的四类网络模式3.1 bridge模式3.2 host模式3.3 container模式3.4 none模式 四.bridge模式下容器的通信4.1 防火墙开启状态4.2 防火墙关闭状态 一.为什么要了解docker网络 当你开始大规模使用Docker时&#xff0…