【C++】—— priority_queue与仿函数

news2024/11/17 1:39:07

【C++】—— priority_queue 与仿函数

  • 1 priority_queue 介绍
  • 2 priority_queue 的使用
    • 2.1 priority_queue 的函数接口
    • 2.2 priority_queue 的使用
  • 3 仿函数
    • 3.1 什么是仿函数
    • 3.2 仿函数的应用
  • 4 需自己写仿函数的情况
    • 4.1 类类型不支持比较大小
    • 4.2 类中支持的比较方式不是我们想要的
  • 5 priority_queue 的模拟实现

1 priority_queue 介绍

  
p r i o i r t prioirt prioirt_ q u e u e queue queue 文档介绍

  • 优先级队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大(或最小)的
  • 此上下文类似于,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于定部的元素)
  • 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类, q u e u e queue queue 提供一组特定的成员函数来访问其元素。元素从特定容器的"尾部"弹出,其称为优先队列的顶部。
  • 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
函数名检测容器是否为空
e m p t y empty empty()检测容器是否为空
s i z e size size()返回容器中有效元素个数
f r o n t front front()返回容器中第一个元素的引用
p u s h push push_ b a c k back back()在容器尾部插入数据
p o p pop pop_ b a c k back back()删除容器尾部元素
  • 标准容器类 v e c t o r vector vector d e q u e deque deque 满足这些需求。默认情况下,如果没有为特定的 p r i o r i t y priority priority_ q u e u e queue queue 类实例化特定容器类,则使用 vector
  • 需要支持随机访问的迭代器,以便始终在内部保存堆结构。容器适配器通过在需要时自动调用算法函数 make_heappush_heappop_heap 来自动完成此操作

  

2 priority_queue 的使用

2.1 priority_queue 的函数接口

  优先级队列默认使用 v e c t o r vector vector 作为其底层存储数据的容器,在 v e c t o r vector vector 上又使用了堆算法 v e c t o r vector vector 中元素构成堆的结构,因此 p r i o r i t y priority priority_ q u e u e queue queue 就是 ,所有需要用到堆的地方,都可以考虑使用 p r i o r i t y priority priority_ q u e u e queue queue
  注意:默认情况下 p r i o r i t y priority priority _ q u e u e queue queue大堆

函数声明接口说明
p r i o r i t y priority priority_ q u e u e queue queue()构造一个空的优先级队列
e m p t y empty empty()检测优先级队列是否为空,是返回 t r u e true true,否则返回 f a l s e false false
t o p top top()返回优先级队列中最大(最小)元素,即堆定元素
p u s h push push( x x x)在优先级队列中插入元素 x x x
p o p pop pop()删除优先级队列中最大(最小)元素,即堆顶元素

  

2.2 priority_queue 的使用

在这里插入图片描述

  优先级队列一个有三个模板参数:class Tclass Container = vector<T>class Compare = less<typename Container::value_type>
  第一个参数是确定优先级队列的存储类型第二个参数确定 p r i o r i t y priority priority_ q u e u e queue queue 底层容器的结构,默认为 v e c t o r vector vector,priority_queue也是一种适配器模式;第三个参数确定是建大堆还是小堆,默认是大堆,建立小堆的话要自己传递一个仿函数。

  我们来简单使用一下 p r i o r i t y priority priority_ q u e u e queue queue

void test1()
{
	priority_queue<int> pq;
	pq.push(4);
	pq.push(1);
	pq.push(5);
	pq.push(7);
	pq.push(9);

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

运行结果:

在这里插入图片描述

  
  我们再来试试小堆

void test2()
{
	priority_queue<int, vector<int>, greater<int>> pq;
	pq.push(4);
	pq.push(1);
	pq.push(5);
	pq.push(7);
	pq.push(9);

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

在这里插入图片描述

  但第三个模板参数class Compare = less<typename Container::value_type> 是什么东西呢?为什么传 g r e a t e r < i n t > greater<int> greater<int> 就从大堆变小堆了呢?这其实是一个仿函数,我们慢慢来介绍。
  
  

3 仿函数

3.1 什么是仿函数

  什么是仿函数呢?仿函数本质是一个 !它里面重载了 o p e r a t o r operator operator() 函数(即函数调用操作符:func()中的())。
  
  比如现在我想写两个整型的比较的仿函数,可以怎么写呢?

class Less
{
public:
	bool operator()(int x, int y)
	{
		return x < y;
	}
};

   可以看到它没有成员变量;其实仿函数大部分都是空类 ,都是没有成员变量
  
  我们将其改造一下就成了模板,可以支持多种类型的比较。但并不是说仿函数就是模板,仿函数类指的是它重载了 o p e r a t o r ( ) operator() 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
}

  按照我们以前的理解,LessFunc(1, 2)是个函数调用,LessFunc是一个函数名或函数指针。但现在,它一个对象
  仿函数本质是一个类,这个类重载 o p e r a t o r ( ) operator() operator()它的对象可以像函数一样使用
  
LessFunc本质是调用了 o p e r a t o r ( ) operator() operator()

cout << LessFunc(1, 2) << endl;
cout << LessFunc.operator()(1, 2) << endl;

  
同样,我们还可以实现一个 g r e a t e r greater greater 的仿函数

template<class T>
class Greater
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x > y;
	}
};

  

3.2 仿函数的应用

  那 p r i o r i t y priority priority_ q u e u e queue queue 为什么要仿函数作为模板参数呢?
  我们知道堆的插入,是要调用向上调整算法

template<class T, class Container = vector<T>>
class priority_queue
{
public:
	void AdjustUp(int child)
	{		
		int parent = (child - 1) / 2;
		while (child > 0)
		{
			if (_con[parent] < _con[child])
			{
				swap(_con[child], _con[parent]);
				child = parent;
				parent = (child - 1) / 2;
			}
			else
				break;
		}
	}

private:
	Container _con;
};

  
  上述实现的向上调整算法,判断条件是if (_con[parent] < _con[child])建的是大堆,那如果我想建小堆怎么办?自己手动改代码吗?那也太离谱了吧。
  
  这时,仿函数的作用就出来了。
  我们再增加一个模板参数: C o m p a r e Compare Compare C o m p a r e Compare Compare 是一个类型传递一个仿函数。我们还可以给一个缺省值

template<class T, class Container = vector<T>, class Compare = Less<T>>

  
  这时,我们就可以将比较逻辑写成泛型

if (Compare(_con[parent], _con[child]))

  
  如果我们想建大堆,比较逻辑是 < ,传递 Less<T> 类型;反之传递 Greater<T> 类型。(库中是 l e s s less less<T> 和 g r e a t e r greater greater<T>)

int main()
{
	Priority_queue<int, vector<int>, Less<int>> p3;
	Priority_queue<int, vector<int>, Greater<int>> p4;

	return 0;
}

  
:模板模板实例化时传递的是类型,而函数模板传参时需要传的是对象

如:写一个向上调整算法的函数模板

template<class Compare>
void AdjustUp(int* a, int child, Compare com)
{
	
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (com(a[child], a[parent]))
		{
			swap(a[child], a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

int main()
{
	int a[] = { 1 };

	Less<int> LessFunc;
	AdjustUp(a, 1, LessFunc);//传递有名对象
	
	AdjustUp(a, 1, Less<int>());//传递匿名对象
	
	return 0;
}

  
  

4 需自己写仿函数的情况

  库中是帮我们实现了仿函数 l e s s less less g r e a t e r greater greater 的,也就是说一般情况下我们是不用自己实现仿函数,这直接调用库里的就好了

在这里插入图片描述

less

在这里插入图片描述

greater

  但有些情况时需要我们自己写的。
  

4.1 类类型不支持比较大小

class Date
{ 
public :
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

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

int main()
{
	priority_queue<Date> q1;
	q1.push(Date(2018, 10, 29));
	q1.push(Date(2018, 10, 28));
	q1.push(Date(2018, 10, 30));
	
	return 0;
}

   D a t e Date Date类 中并没有重载 o p e r a t o r operator operator< 和 o p e r a t o r operator operator> 的函数,编译就会报错

在这里插入图片描述

  这时,就需要我们自己实现 l e s s less less g r e a t e r greater greater 仿函数
  

4.2 类中支持的比较方式不是我们想要的

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);
	}
private:
	int _year;
	int _month;
	int _day;
};

  
  现在 D a t e Date Date类 中支持了比较方式,但如果我们这样传参呢?

int main()
{
	priority_queue<Date*> q1;
	q1.push(new Date(2018, 10, 29));
	q1.push(new Date(2018, 10, 28));
	q1.push(new Date(2018, 10, 30));
	
	cout << *q1.top() << endl;
	q1.pop();
	cout << *q1.top() << endl;
	q1.pop();
	cout << *q1.top() << endl;
	q1.pop();

	return 0;
}

在这里插入图片描述

  你会发现,每次的结果都不一样,我们控制不住。这时因为我们传递的是指针,它是按指针大小来比较

  这时就需要我们自己实现仿函数

class DateLess
{
	bool operator()(Date* p1, Date* p2)
	{
		return *p1 < *p2;
	}
};

  

5 priority_queue 的模拟实现

namespace my_priority_queue
{
   
    template <class T, class Container = vector<T>, class Compare = less<T> >
    class priority_queue
    {
    public:
       template <class InputIterator>
       priority_queue(InputIterator first, InputIterator last)
       {
           InputIterator it = first;
           while (it != last)
           {
               push(*it);
               ++it;
           }
       }

        priority_queue() {}
        

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

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

        const T& top() const
        {
            return _c.front();
        }

        T& top()
        {
            return _c.front();
        }

        void push(const T& x)
        {
            _c.push_back(x);
            AdjustUp(size() - 1);
        }

        void AdjustUp(int child)
        {          
            int parent = (child - 1) / 2;

            while (child > 0)
            {
                if (_comp(_c[parent], _c[child]))
                {
                    swap(_c[parent], _c[child]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else
                    break;
            }
        }

        void AdjustDown(int parent, int end)
        {
            int child = parent * 2 + 1;

            while (child <= end)
            {
                if (child + 1 <= end && _comp(_c[child], _c[child + 1]))
                    ++child;

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

        void pop()
        {
            assert(!empty());

            std::swap(_c[0], _c[size() - 1]);
            _c.pop_back();

            AdjustDown(0, size() - 1);
        }

    private:
        Container _c;
        Compare _comp;
    };
}

  

  
  
  


  好啦,本期关于 priority_queue 与仿函数 的知识就介绍到这里啦,希望本期博客能对你有所帮助。同时,如果有错误的地方请多多指正,让我们在 C++ 的学习路上一起进步!

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

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

相关文章

多模态——基于XrayGLM的X光片诊断的多模态大模型

0.引言 近年来&#xff0c;通用领域的大型语言模型&#xff08;LLM&#xff09;&#xff0c;如ChatGPT&#xff0c;已在遵循指令和生成类似人类的响应方面取得了显著成就。这些成就不仅推动了多模态大模型研究的热潮&#xff0c;也催生了如MiniGPT-4、mPLUG-Owl、Multimodal-G…

在vscode在使用idea编辑器的快捷键

在vscode在使用idea编辑器的快捷键 在vscode扩展在搜索idea key结果如下&#xff1a; 选择IntelliJ IDEA Keybindings安装&#xff08;注意作者是Keisuke Kato&#xff09;&#xff0c;安装后就可以在vscode编辑器中使用idea编辑器的快捷键。

Fastadmin 前台任意文件读取漏洞

漏洞描述 FastAdmin是一个基于ThinkPHP5和Bootstrap的后台开发框架&#xff0c;支持权限管理、响应式开发、多语言、模块化开发、CRUD和自由可扩展等功能。 漏洞复现 FOFA body"fastadmin.net" || body"<h1>fastadmin</h1>" && tit…

c语言200例 066

大家好&#xff0c;欢迎来到无限大的频道 今天给大家带来的是c语言200例。 要求&#xff1a; 根据输入的职业表示&#xff0c;区分是老师还是学生&#xff0c;然后根据输入的信息&#xff0c;将对应的信息输出&#xff0c;如果是学生&#xff0c;则输出班级&#xff0c;如果是…

xlsx库插件读取excel文件

input读取xlsx文件内容 效果代码 前端用input读取 .xlsx文件的内容 xlsx库参考连接 项目中我用的ant-design-vue&#xff0c;不过用input一样的大同小异 注意区分xlsx库和node-xlsx库的使用环境 效果 代码 <!--* Descripttion: * Author: 苍狼一啸八荒惊* Date: 2024-08-…

英文翻译中文怎么做?试试这四款翻译工具!

日常工作中&#xff0c;我时常需要处理各种学术资料&#xff0c;其中不乏英文文献。在这个信息爆炸的时代&#xff0c;翻译工具成为了我们不可或缺的好帮手。今天&#xff0c;我想和大家分享几款我常用的翻译工具&#xff0c;它们在英文翻译中文方面的表现&#xff0c;真的让我…

学习鸿蒙Harmong基础(二)

1.类声明和使用 class Perpon { name : string "小赵"; age : number 24; isShow :boolean true; // 构造函数 constructor(name:string,age:number,isShow:boolean){ this.name name; this.age age; this.isShow isShow } puperyInfo(){ if (this.isShow) { …

Chainlit集成LlamaIndex实现知识库高级检索(子问题查询引擎)

检索原理 llama_index 的 SubQuestionQueryEngine 是一个用于处理复杂查询的机制&#xff0c;它的主要目的是将复杂的查询问题分解成多个较小的、更容易管理和处理的子问题。这种技术有助于提高查询效率和准确性&#xff0c;尤其是在处理大量文档或者需要多步骤推理的情况下。…

2024年【金属非金属矿山(露天矿山)安全管理人员】最新解析及金属非金属矿山(露天矿山)安全管理人员考试试卷

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 金属非金属矿山&#xff08;露天矿山&#xff09;安全管理人员最新解析参考答案及金属非金属矿山&#xff08;露天矿山&#xff09;安全管理人员考试试题解析是安全生产模拟考试一点通题库老师及金属非金属矿山&#…

安装 Nacos 启动报错 java.lang.IllegalArgumentException: db.num is null

java.io.IOException: java.lang.IllegalArgumentException: db.num is nullat com.alibaba.nacos.config.server.service.datasource.ExternalDataSourceServiceImpl.reload(ExternalDataSourceServiceImpl.java:130)解决办法&#xff1a; 编辑 startup.cmd 文件 找到 set MO…

计算机毕业设计 基于Python的热门微博数据可视化分析系统的设计与实现 Python+Django+Vue 可视化大屏 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

海外盲盒APP系统:盲盒出海热潮下的选择

近年来&#xff0c;盲盒市场展现出了强劲的发展态势&#xff0c;不仅在国内持续上演“盲盒热”&#xff0c;在海外市场中更是“一盒难求”&#xff01;在盲盒出海的浪潮下&#xff0c;盲盒在国际市场中迅速火爆&#xff0c;一时席卷了全球市场。 海外盲盒系统是企业拓展海外市…

c++模拟真人鼠标轨迹算法

一.鼠标轨迹算法简介 鼠标轨迹底层实现采用 C / C语言&#xff0c;利用其高性能和系统级访问能力&#xff0c;开发出高效的鼠标轨迹模拟算法。通过将算法封装为 DLL&#xff08;动态链接库&#xff09;&#xff0c;可以方便地在不同的编程环境中调用&#xff0c;实现跨语言的兼…

没有建筑工程资质,怎么去投标?

在建筑工程领域&#xff0c;没有资质的企业想要参与投标&#xff0c;可以考虑以下几种方式&#xff1a; 1. 资质借用&#xff1a;可以与具有相应资质的企业合作&#xff0c;通过资质借用的方式参与投标。但这种做法存在法律风险&#xff0c;因为《中华人民共和国招标投标法》明…

kubernetes配置资源管理

目录 一、Secret1.1 Secret 四种类型1.2 Secret 使用条件 二、Secret 使用方式2.1 基于Opaque创建 Secret2.2 内容用 base64 编码&#xff0c;创建Secret2.3 将 Secret 挂载到 Volume 中&#xff0c;以 Volume 的形式挂载到 Pod 的某个目录下2.4 将 Secret 导出到环境变量中2.5…

【Python】Pythonic Data Structures and Algorithms:深入浅出数据结构与算法的 Python 实现

Pythonic Data Structures and Algorithms 是一个开源项目&#xff0c;汇集了各种经典数据结构和算法的 Python 实现。该项目旨在为开发者提供丰富的学习资源&#xff0c;帮助他们通过 Python 代码理解和掌握数据结构与算法的核心原理和应用。项目中的算法涵盖了排序、搜索、图…

南平自闭症寄宿制学校:让孩子自信绽放

在繁华与喧嚣交织的都市之中&#xff0c;有一片静谧而充满希望的土地——广州星贝育园自闭症儿童寄宿制学校&#xff0c;这里不仅是知识的殿堂&#xff0c;更是自闭症儿童心灵成长的温馨家园。星贝育园&#xff0c;以其独特的教育理念与细致入微的关怀&#xff0c;为这些特殊的…

初始爬虫9

1.元素定位后的操作 “find_element“仅仅能够获取元素&#xff0c;不能够直接获取其中的数据&#xff0c;如果需要获取数据需要使用以下方法”。下面列出了两个方法&#xff1a; 获取文本 element.text 通过定位获取的标签对象的 text 属性&#xff0c;获取文本内容 获取属性…

opencv实战项目二十九:GrabCut分割人像

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、GrabCut介绍&#xff1a;二、opencv实现&#xff1a;三、效果&#xff1a; 前言 在数字图像处理领域&#xff0c;人像分割是一项极具挑战性的任务&#xf…

二维数组的使用

本章我将用自己的语言给大家翻译二维数组的使用&#xff0c;要是因为我阐述的不清晰&#xff0c;大家不懂的的可以直接在评论里问。 1.下标 二维数组的下标和一维数组没有多大的区别&#xff0c;唯一的区别就是&#xff0c;一维数组只有列&#xff0c;而二维数组还有行 一维数…