【STL】priority_queue的底层原理及其实现

文章目录

  • priority_queue的介绍
  • 库中priority_queue的使用
    • 什么叫仿函数?
  • 模拟实现prioprity_queue类

priority_queue的介绍

在这里插入图片描述
解释以上内容

  1. priority_queue(优先级队列)跟stack、queue一样,都是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大或者最小的(默认最大)。
  2. .优先队列的底层数据结构是用堆来实现的。
  3. 作为容器适配器,priority_queue默认是由 vector类
    来实现的。优先队列(堆)这种数据结构需要快速随机访问元素的能力。除了vector,deque类也可以用来作为优先队列的底层容器。严格来说,只要满足以下能力的容器都可以作为优先队列的底层容器:
    (1)empty():检测容器是否为空
    (2)size():返回容器中有效元素个数
    (3)front():返回容器中第一个元素的引用
    (4)push_back():在容器尾部插入元素
    (5)pop_back():删除容器尾部元素

库中priority_queue的使用

虽然我们将priority_queue称为优先队列,但是其本质就是堆,而堆的本质又是一颗完全二叉树。所以,我们需要一种符合以上这些数据结构所有特点的容器来存储元素。vector就显得非常合适。可以用下标映射节点的父子关系,高效尾删,快速随机访问。

现在我们来看库中priority_queue主要有哪些功能吧

函数声明接口说明
priority_queue()和priority_queue(first, last)构造一个空的优先队列
empty()检测优先级队列是否为空,是返回true,否则返回false
top()返回堆顶元素的值,即当前优先队列中的最大值或者最小值
push(val)在优先队列中插入val,并且调整优先队列
pop()弹出堆顶元素,即优先队列的最大值或者最小值

1.使用演示:创建大堆,默认就是最大堆在这里插入图片描述
2.使用演示:创建最小堆, 将第三个模板参数换成greater比较方式
在这里插入图片描述
其中,greater是一个类模板,实例化出来之后可以当函数一样使用(仿函数)。greater里面控制了比较对象的关系,是一个比较剂。

什么叫仿函数?

仿函数又叫函数对象。本质是一个类实例化出来的对象,因为重载了()我们可以像使用函数一样使用其operator()
比如下面就是一个仿函数:

class fun {
public:
	int operator()(int& A, int& B) {
		return A + B;
	}

};

我们可以创建一个fun类来调用operator():

class fun {
public:
	int operator()(int& A, int& B) {
		return A + B;
	}
};
void test3() {
	fun f;
	int a = 1;
	int b = 2;
	cout << f(a, b) << endl;
}

借用模板之后,仿函数比普通函数显得更加灵活
priority_queue<int, vector<int>, greater<int>>中的greater其实就是一个函数对象,通过实例化int类型,来控制两个int类型变量的顺序关系。
模拟实现greater类:

template<class T>
class Greater {
public:
	bool operator()(T& A, T& B) {
		return A > B;
	}
};

当我们将greater作为第三个参数传递给priority_queue时,二叉树的父子节点的关系也就确定了,即确定了优先级。

那为什么默认是小堆呢?
在这里插入图片描述
这是因为priority_queue的第三个参数的缺省值设置成了less,而less其实就是一个仿函数,控制了堆顶元素永远是最小值。

当然,我们同样可以不用仿函数做参数,用一个函数指针也可以达到一样的效果

值得注意的是,如果priority_queue的元素是自定义类型,那我们需要在自定义类型中提供比较运算符的重载,以便达到我们想要的目的
给出以下代码示例:

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;
};
 
void TestPriorityQueue()
{
 // 大堆,需要用户在自定义类型中提供<的重载
 priority_queue<Date> q1;
 q1.push(Date(2018, 10, 29));
 q1.push(Date(2018, 10, 28));
 q1.push(Date(2018, 10, 30));
 cout << q1.top() << endl;
 
 // 如果要创建小堆,需要用户提供>的重载
 priority_queue<Date, vector<Date>, greater<Date>> q2;
 q2.push(Date(2018, 10, 29));
 q2.push(Date(2018, 10, 28));
 q2.push(Date(2018, 10, 30));
 cout << q2.top() << endl;
}

模拟实现prioprity_queue类

#define _CRT_SECURE_NO_WARNINGS 1
#include<vector>
#include<algorithm>

template<class T>
class Less {
public:
	bool operator()(T& A, T& B) {
		return A < B;
	}

};

template<class T>
class Greater {
public:
	bool operator()(T& A, T& B) {
		return A > B;
	}

};



template<class T,class Container=std::vector<T>, class Compare = Less<T> >
class priority_queue {
public:
	int size() {
		return _con.size();
	}
	bool empty() {
		return _con.empty();
	}

	void adjust_up(int n) {
		int child = n;
	    int father = (n - 1) / 2;
		Compare com;
		while (child >= 0) {
			if (com(_con[father], _con[child])) {
				std::swap(_con[father], _con[child]);
				child = father;
				father = (child - 1) / 2;
			}
			else {	
				break;
			}
		}
	}

	void adjust_down(int n) {
		int father = n;
		int child = father * 2 + 1;
		Compare com;
		while (child < size()) {
			if (child + 1 < size() && com(_con[child ],_con[child+1])) {
				child++;
			}

			if (com(_con[father], _con[child])) {
				std::swap(_con[father], _con[child]);
				father = child;
				child = child * 2 + 1;
			}
			else {
				break;
			}

		}
	}

	void push(const T& val) {
		_con.push_back(val);
		adjust_up(_con.size()-1);
	}

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

	void pop() {
		std::swap(_con[0], _con[size() - 1]);
		_con.pop_back();
		adjust_down(0);
	}
	T& operator[](size_t n) {
		return _con[n];
	}
private:
	Container _con;
};


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

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

相关文章

SpringBoot中定时任务踩坑,@Scheduled重复执行问题排查(看完直接破防)

前言 今天再开发业务需求的过程中&#xff0c;需要用到定时任务&#xff0c;原本定的是每10分钟推送一次&#xff0c;可是当每次十分钟到的时候&#xff0c;定时任务就会推送多条&#xff01;但是非常奇怪的是&#xff0c;本地调试的时候不会有问题&#xff0c;只有当你部署到…

OpenCV | 图像读取与显示

OpenCV 对图像进行处理时&#xff0c;常用API如下&#xff1a; API描述cv.imread根据给定的磁盘路径加载对应的图像&#xff0c;默认使用BGR方式加载cv.imshow展示图像cv.imwrite将图像保存到磁盘中cv.waitKey暂停一段时间&#xff0c;接受键盘输出后&#xff0c;继续执行程序…

windows 之 redis非安装版,启动与初始化密码

1、下载redis 免安装版 2、解压后&#xff0c;启动服务 3、双击客服端 4、设置密码 config set requirepass root123456成功后&#xff0c;退出服务再次双击 5、登录 再次执行命名时已经没权限了 使用 auth password 登录 成功后&#xff0c;就可以了 auth root123456 …

arcgis使用面shp文件裁剪线shp文件报错

水系数据裁剪&#xff0c;输出为空&#xff1a; ArcGIS必会的几个工具的应用 --提取、分割、融合、裁剪&#xff08;矢&#xff09;、合并、追加、镶嵌、裁剪&#xff08;栅&#xff09;、重采样_arcgis分割-CSDN博客 下面的方法都不行&#xff1a; ArcGIS Clip&#xff08;裁…

JavaScript - 你遇到过哪几种Javascript的错误类型

难度级别:中级及以上 提问概率:50% 我们在开发Javascript代码的时候,经常一不小心就会遇到各种各样的异常,浏览器也会及时给出错误信息,那么一般会遇到哪几种异常情况呢,我们来看一下。 1 ReferenceError错误 ReferenceError几乎是最…

Ubuntu 20.04.06 PCL C++学习记录(二十四)

[TOC]PCL中点云分割模块的学习 学习背景 参考书籍&#xff1a;《点云库PCL从入门到精通》以及官方代码PCL官方代码链接,&#xff0c;PCL版本为1.10.0&#xff0c;CMake版本为3.16&#xff0c;可用点云下载地址 学习内容 如何使用已知系数的 SAC_Models 从点云中提取参数模型…

Tomcat 获取客户端真实IP X-Forwarded-For

Tomcat 获取客户端真实IP X-Forwarded-For 代码实现&#xff1a; 在Host标签下面添加代码&#xff1a; <Valve className"org.apache.catalina.valves.RemoteIpValve" remoteIpHeader"x-forwarded-for" remoteIpProxiesHeader"x-forwarded-by&q…

面试算法-165-随机链表的复制

题目 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都设为其对应的原节点的值。新节…

网络安全---非对称数据加密签名验证

一、课题描述 三位同学一组完成数据的非对称加密和数字签名验证传输。 三位同学分别扮演图中 Alice、Bob 和 CA 三个角色&#xff0c;Bob 和 Alice 从 CA 中获得数字证书、Bob 向 Alice 发送秘密发送一段加密并签名后的信息&#xff0c;Alice 获取 Bob 发送的加密信息&#x…

关于ARM的一些问题

一&#xff0c;arm的工作模式有哪些&#xff1f; User&#xff1a;非特权模式 FIQ&#xff1a;高优先级中断进入 IRQ&#xff1a;低优先级中断进入 Supervisor:当复位或软中断指令进入 Abort: 当存取异常时 Undef:当执行未定义指令时会进入这种模式 System:使用和User模式相同…

科技云报道:从“奇点”到“大爆炸”,生成式AI开启“十年周期”

科技云报道原创。 世界是复杂的&#xff0c;没有人知道未来会怎样&#xff0c;但如果单纯从技术的角度&#xff0c;我们总是能够沿着技术发展的路径&#xff0c;找到一些主导未来趋势的脉络。 从Sora到Suno&#xff0c;从OpenAI到Copilot、Blackwell&#xff0c;这些热词在大…

【Redis】底层跳表实现

先巩固Redis的数据类型以及底层的数据结构&#xff1a; ZSet&#xff08;有序集合&#xff09;可以使用两种不同的内部数据结构来表示&#xff1a;压缩列表&#xff08;ziplist&#xff09;和跳跃表&#xff08;skiplist&#xff09;。 跳表是redis底层SortedSet(ZSet)的数据…

JAVA并发编程(二)_线程池

JAVA线程池 1.1Java 线程池之 Executor 框架 为了实现线程池和管理线程池&#xff0c;JDK 给我们提供了基于 Executor 接口的一系列接口、抽象类、实现类&#xff0c;我们把它称作线程池的 Executor 框架&#xff0c;Executor 框架本质上是一个线程池&#xff1b; ​ Java 线…

Linux LVM磁盘扩容

1、查看磁盘情况 df -h df -h2、查看逻辑卷 lvdisplay lvdisplay3、查看逻辑组 vgdisplay vgdisplay4、查看物理卷 pvdisplay pvdisplay5、查看磁盘 fdisk -l fdisk -l6、磁盘分区fdisk /dev/磁盘名 # 上一步查看到的新硬盘路径 fdisk /dev/vdb7、格式化磁盘mkfs -t ext4…

【负载均衡——一致性哈希算法】

1.一致性哈希是什么 一致性哈希算法就很好地解决了分布式系统在扩容或者缩容时&#xff0c;发生过多的数据迁移的问题。 一致哈希算法也用了取模运算&#xff0c;但与哈希算法不同的是&#xff0c;哈希算法是对节点的数量进行取模运算&#xff0c;而一致哈希算法是对 2^32 进…

基于SSM+Jsp+Mysql的超市管理系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

图片管理系统:原理、设计与实践

title: 图片管理系统&#xff1a;原理、设计与实践 date: 2024/4/9 20:04:25 updated: 2024/4/9 20:04:25 tags: 图片管理存储组织上传采集处理编辑搜索检索展示分享AI应用 第一章&#xff1a;图片管理系统概述 1.1 图片管理系统简介 图片管理系统是一种用于存储、组织、处理…

【Java网络编程】IP网络协议与TCP、UDP网络传输层协议

1.1、IP协议 当应用层的数据被封装后&#xff0c;想要将数据在网络上传输&#xff0c;数据究竟要被发往何处&#xff0c;又该如何精准的在网络上定位目标机器&#xff0c;此时起到关键作用的就是“IP协议”。IP协议的作用在于把各种数据包准确无误的传递给目标方&#xff0c;其…

力扣HOT100 - 56. 合并区间

解题思路&#xff1a; class Solution {public int[][] merge(int[][] intervals) {// 先按照区间起始位置排序Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);int[][] res new int[intervals.length][2];int idx -1;for (int[] interval : intervals) {//直接加入的…

前端实现打开新标签页后,再次定位到该标签页

需求 A 页面中点击按钮可以打开新的标签页 B 并且向 B 页面发送消息数据。 当新的标签页 B 未关闭且符合同源策略时&#xff0c;再次点击按钮&#xff0c;可以自动跳转到标签页 B 并且发生消息数据。 B.html <script>window.onmessage evt > {console.log(evt.d…