【C++】优先级队列 priority_queue的使用模拟实现 | 仿函数

news2024/12/24 8:57:04

🌈欢迎来到C++专栏~~优先级队列的使用 & 模拟实现


  • (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort
  • 目前状态:大三非科班啃C++中
  • 🌍博客主页:张小姐的猫~江湖背景
  • 快上车🚘,握好方向盘跟我有一起打天下嘞!
  • 送给自己的一句鸡汤🤔:
  • 🔥真正的大师永远怀着一颗学徒的心
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏
  • 🎉🎉欢迎持续关注!
    在这里插入图片描述

请添加图片描述

list的使用

  • 🌈欢迎来到C++专栏~~优先级队列的使用 & 模拟实现
    • 一. 优先级队列的使用
    • 一. priority_queue的模拟实现
      • 🌈size & empty & top
      • 🌈仿函数
        • 🧐优缺点:
        • 🎨push 和向上调整算法
        • 🎨pop 和向下调整算法
      • 构造函数
      • 如果T是自定义类型
  • 📢写在最后

请添加图片描述

优先级队列也是一种 容器适配器,默认情况下它适配的是vector,以支持 堆的算法中频繁的随机访问。下面详细讲解

一. 优先级队列的使用

头文件:<queue>

在这里插入图片描述

  • Container :默认情况下,它适配的是vector(因为要大量用到[]找下标)。理论上底层的容器可以是任何标准容器类模板,也可以是其他特定设计的容器类,但是必须支持随机访问迭代器访问,以及一系列基本接口。
  • Compare:默认情况下,大的优先级高(即默认是大堆),仿函数给的是less(这确实有点奇怪)。小堆需要传入仿函数类型,它的头文件在<functional>
void test_priority_queue()
{
	//默认大的优先级高
	priority_queue<int> pq;
	pq.push(3);
	pq.push(1);
	pq.push(2);
	pq.push(5);
	pq.push(9);
	pq.push(0);

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

	int a[] = { 3, 1, 5, 7, 4, 0, 3, 2 };
	priority_queue<int> heap(a, a + sizeof(a) / sizeof(int));//区间初始化

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

在这里插入图片描述

多说无益,做一道题目上手:215. 数组中的第K个最大元素

方法一:建大堆(堆排) + pop (k-1)次取堆顶

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

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        //建堆  O(N)
        priority_queue<int> maxHeap(nums.begin(), nums.end());

        //O(logN* K)
        while(--k)
        {
            maxHeap.pop();
        }
        return maxHeap.top();
    }
};

一. priority_queue的模拟实现

priority_queue框架,他的底层是一个随机容器,模板第三个参数(仿函数)后面详谈——

🌈size & empty & top

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

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

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

🌈仿函数

灵活控制的开关(仿函数):是排大堆还是小堆,总不可能改代码吧

我们写一个类,没有任何成员变量,重载了operator()运算符 ——

//仿函数/函数对象 ---- 类 重载了operator()
//类对象可以像函数一样去使用
namespace ljj
{
	template<class T>
	class less
	{
	public:
		bool operator()(const T& l, const T& r) const
		{
			return l < r;
		}
	};

	template<class T>
	class greater
	{
	public:
		bool operator()(const T& l, const T& r) const
		{
			return l > r;
		}
	};
}

	priority_queue<int>  heap(a, a + sizeof(a) / sizeof(int));
	priority_queue<int, vector<int>, greater<int>> heap(a, a + sizeof(a) / sizeof(int));

不同仿函数类型的对象,用()来调用对应的函数比较方式,就实现了像函数一样调用 ——

int main()
{
	ljj::less<int> lsFunc;

	cout << lsFunc(1, 2) << endl;
	//等价于下面
	//cout << lsFunc.operator()(1, 2) << endl;

	ljj::greater<int> gtFunc;
	cout << gtFunc(1, 2) << endl;

	return 0;
}

🧐优缺点:

🌍优点如下:

  • 在同一时间里,由某个仿函数所代表的单一函数,可能有不同的状态(可以根据不同的类型代表不同的状态

  • 仿函数即使定义相同,也可能有不同的类型(可以有不同类型)

  • 仿函数使程序代码变简单(仿函数在一定程度上使代码更通用,本质上简化了代码)

🌍缺点:

  • 仿函数通常比一般函数速度

🎨push 和向上调整算法

优先级队列的插入及删除,就是在堆的基础上做插入删除,学到这我还回去复习了一下堆

堆插 = 尾插+ 向上调整

	//插入 —— 向上调整
	void push(const T& x)
	{
		_con.push_back(x);
		adjust_up(_con.size()-1);
	}

🍅重头戏:向上调整算法 (看图5分钟内写出来,才可以)
在这里插入图片描述

//向上调整
void adjust_up(size_t child)
{
	Compare com;//仿函数
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		//if ( _con[child] > _con[parent])
		if (com(_con[parent] , _con[child]))
		{
			std::swap(_con[child], _con[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

🎨pop 和向下调整算法

堆删 = 交换 + 删除 + 向下调整(不会破坏树的结构)

	//交换 —— 删除 —— 向下调整
	void pop()
	{
		std::swap(_con[0], _con[_con.size() - 1]);
		_con.pop_back();
		
		adjust_down(0);
	}

💦向下调整算法
在这里插入图片描述

//高度次  logN		
void adjust_down(size_t parent)
{
	Compare com;//仿函数
	size_t child = parent * 2 + 1;//左孩子
	while (child < _con.size())
	{
		//左右孩子选大的交换
		//if (child + 1 < size() && _con[child + 1] > _con[child])
		if (child + 1 < size() && com(_con[child], _con[child + 1]))
		{
			++child;
		}

		//if (_con[child] > _con[parent])
		if (com(_con[parent], _con[child]))
		{
			std::swap(_con[child], _con[parent]);
			parent = child;
			child = parent + 1;
		}
		else
		{
			break;
		}
	}

}

建大堆还是建小堆本质是由于ajust_up和ajust_down中的比较符号不同,那么就要通过仿函数来控制

在这里插入图片描述

构造函数

🌈 迭代器区间构造:_con自定义类型会调用它的迭代器区间构造,不用再迭代器遍历+push了。在这基础上还要构建成堆,为了使左右都是大(小)堆且向下调整是O(N),要倒着建,从最后一个非叶子节点(即最后一个节点的父亲)开始向下调整

priority_queue()
{}

//迭代器区间构造
template<class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{
	while (first != last)
	{
		_con.push_back(*first);
		++first;
	}

	//向下调整 建堆
	for (int i = (_con.size() - 1 - 1) / 2; i >= 0; --i)
	{
		adjust_down(i);
	}
}

如果T是自定义类型

⚡我们考虑到如果T 是日期类Date等 —— 要看情况

  • 如果是类里面有支持比较大小的,就直接用 比如:string
  • 如果是库里面的、人家写好的类,我们只能写仿函数,我们不可能改人家的代码,如果是自己写的类,二者都可以(看情况)
  • 但有些情况是必须写仿函数的,因为原生比较大小的行为不一定是我们想要的,比如:Date*比较大小,但是我们不想用指针比较,那就写仿函数
	void test_priority_queue()
	{
		//priority_queue<Date> pq;
		priority_queue<Date, vector<Date>, greater<Date>> pq;
		pq.push(Date(2022, 3, 26));
		pq.push(Date(2022, 4, 1));
		pq.push(Date(2022, 2, 19));
		pq.push(Date(2022, 4, 10));

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

我们当然可以重载这些运算符

但是如果数据类型 不支持比较 或 比较的方式不是你想要的,可以自己实现仿函数,按照你想要的方式(Compare给我们留的口子)去控制比较逻辑,比如 —— 我想比较地址大小:Date*

	void test_priority_queue3()
	{
		//priority_queue<Date*> pq; //默认比较地址大小
		//priority_queue<Date*, vector<Date*>, lessPDate> pq;
		priority_queue<Date*, vector<Date*>, greaterPDate> pq;
		pq.push(new Date(2022, 3, 26));
		pq.push(new Date(2022, 4, 1));
		pq.push(new Date(2022, 2, 19));
		pq.push(new Date(2022, 4, 10));

		//默认比较地址大小,若想比较日期大小,自己写仿函数
		while (!pq.empty())
		{
			cout << *pq.top() << endl;
			pq.pop();
		}
	}

于是我们自己写了仿函数,又因为私有成员类外无法访问,我们把这两个仿函数类声明为priority_queue的友元类 ——

	struct lessPDate
	{
		bool operator()(const Date* d1,const Date* d2)
		{
			//return *d1 < *d2;
			return (d1->_year < d2->_year) ||
				(d1->_year == d2->_year && d1->_month < d2->_month) ||
				(d1->_year == d2->_year && d1->_month == d2->_month && d1->_day < d2->_day);
		}
	};

	struct greaterPDate
	{
		bool operator()(const Date* d1, const Date* d2)
		{
			//return *d1 > *d2;
			return (d1->_year > d2->_year) ||
				(d1->_year == d2->_year && d1->_month > d2->_month) ||
				(d1->_year == d2->_year && d1->_month == d2->_month && d1->_day > d2->_day);
		}
	};

📢写在最后

好奇,我偷偷溜出去,都能被辅导员发现,表情如下

在这里插入图片描述

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

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

相关文章

聚乙二醇衍生物MAL-PEG-NH2,Maleimide-PEG-amine,CAS:1207751-12-9​

英文&#xff1a;MAL-PEG-NH2&#xff0c;Maleimide-PEG-amine 中文&#xff1a;马来酰亚胺-聚乙二醇-氨基 2、CAS编号&#xff1a;1207751-12-9 3、所属分类&#xff1a;Amine PEG Maleimide PEG 4、分子量&#xff1a;可定制&#xff0c;5000、2000、3400、20000、10000、…

【附源码】Python计算机毕业设计汽车租赁网站

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

eBPF学习笔记(一)—— eBPF介绍内核编译

eBPF学习笔记&#xff08;一&#xff09;—— eBPF介绍&内核编译eBPF介绍eBPF基础代码验证即时编译eBPF开发工具BCCbpftracelibbpf C/C库eBPF Go库内核编译查看内核版本下载内核源码安装依赖项安装最新版llvm和clang配置内核编译内核BPF示例程序常见问题问题一&#xff1a;…

C++ 堆、大顶堆、小顶堆、堆排序

目录一、什么是堆&#xff1f;1.1 大顶堆1.2 小顶堆1.3 自定义greater1.4 堆索引的特点1.5 堆操作添加元素删除最大元素检查序列是否是堆检查部分序列为堆对堆进行排序升序降序问题&#xff1a;sort()和sort_heap()有什么区别二、排序算法&#xff1a;堆排序2.1 堆排序原理创建…

Bootstrap框架(全)

Bootstrap中文网&#xff1a;点我直达 Bootstrap菜鸟教程&#xff1a;点我直达 目录&#xff1a;&#xff08;一&#xff09;Bootstrap标准模板&#xff1a;&#xff08;二&#xff09;CSS样式区&#xff1a;一、布局容器&#xff1a;二、栅格系统&#xff1a;1.列组合&#x…

<Linux> 软件包管理器yum及编辑器vim的使用

文章目录一、yum的背景知识1. 商业生态2. 开源生态3. 软件生态本土化二、yum 的基本使用1. 查看软件包2. 安装、卸载、搜索软件实现Windows下和Linux下文件互传三、vim的基本使用1. vim 的基本概念2. vim 的基本操作2.1.模式间切换2.2.插入模式2.3.命令模式2.4.底行模式四、vim…

nginx(四十二)access阶段的模块学习

一 ngx_http_access ① 基本描述 场景&#xff1a; 限制某些ip来源的访问;基于ip进行限制细节点&#xff1a; 基于$remote_addr进行判断的 ② allow deny ③ 官方案例 说明&#xff1a; 按照配置文件中allow|deny的先后顺序,只要匹配上则停止继续匹配 ④ 经典应用场…

Rust机器学习之ndarray

Rust机器学习之ndarray 众所周知&#xff0c;Python之所以能成为机器学习的首选语言&#xff0c;与其丰富易用的库有很大关系。某种程度上可以说是诸如numpy、pandas、scikit-learn、matplotlib、pytorch、networks…等一系列科学计算和机器学习库成就了Python今天编程语言霸主…

Vue项目实战——【基于 Vue3.x + NodeJS】实现的课程表排课系统二(week-title)

文章目录基于 Vue3.x NodeJS实现的课程表排课系统&#xff08;二&#xff09;初始化样式封装axios处理数据表格头部&#xff08;周几&#xff09;子组件写入根组件App.vue浅拿一下数据基于 Vue3.x NodeJS实现的课程表排课系统&#xff08;二&#xff09; 初始化样式 src/ass…

当 AI 遇上 web3,会碰撞出什么火花?

2020 年之前&#xff0c;Web3 的路是创造者们铺好的。但 Web3 遇上了金融&#xff0c;这出乎了每个创造者的意料之外&#xff0c;稳定币、AMM 和借贷突其来地点燃了2020年的那个夏天。之后 Web3 又遇到了 NFT、游戏和元宇宙。不过因为技术限制&#xff0c;除了金融之外&#xf…

AVL树详解(附带旋转步骤图,手把手带你上分)

文章目录&#x1f44d; AVL树的概念先了解一下&#x1f601;AVL树节点的定义&#x1f60a;AVL树插入节点&#x1f91e;AVL树为什么要旋转&#x1f60d;AVL树的四种旋转左单旋右单旋左右双旋右左双旋❤️结语关于AVL树的讲解 &#x1f44d; AVL树的概念先了解一下 它的左右子树…

Scala005--Scala中的数据结构【集合】之数组

Scala中的数据结构和Java中一样&#xff0c;都有数组&#xff0c;列表&#xff0c;集合&#xff0c;映射。在Scala中与Java不同的是数组可以有可变数组&#xff0c;而不是一旦定义就不可以进行更改。我们来认识数组&#xff0c;并使用相应的代码去查看具体的实现效果。 目录 …

Linux命令从入门到实战 ----进程管理

文章目录kill终止进程pstree查看进程树netstat网络状态和端口占用信息crontab定时任务进程(process): 是正在执行的一个程序或命令&#xff0c;每一个进程都是一个运行的实体&#xff0c;都有自己的地址空间&#xff0c;并占用一定的系统资源 服务(service)&#xff1a;启动之后…

深度学习 Day 18——利用卷神经网络实现猫狗识别 Ⅱ

深度学习 Day 18——利用卷神经网络实现猫狗识别 Ⅱ 文章目录深度学习 Day 18——利用卷神经网络实现猫狗识别 Ⅱ一、前言二、我的环境三、前期工作1、导入依赖项并设置GPU2、导入数据四、数据预处理1、加载数据2、检查数据3、配置数据集并进行归一化处理4、可视化数据五、构建…

xv6源码解析(三)——内存管理

01 内存管理 内存管理&#xff1a;通过编写物理页分配器&#xff0c;以链表管理所有空闲页帧&#xff0c; 实现了对物理页帧的回收与分配&#xff1b;在xv6系统sbrk内存管理方式的基础上&#xff0c;添加了进程用户空间非连续分区的分配。 内存管理参考链接 mmap 02 sbrk机制…

猿创征文|【Linux】Linux中的gdb调试器的使用

目录 一、什么是gdb&#xff1f; 二、gdb的安装 三、gdb的使用 1、只有debug版可以被调试 2、gdb的常用指令 2.1显示代码&#xff1a;l指令&#xff08;list指令&#xff09; 2.2设置断点&#xff1a;b指令&#xff08;break指令&#xff09; 2.3显示断点/禁用断点/开启…

Linux操作系统~进程fork到wait到底怎么用?

目录 1.fork() &#xff08;1&#xff09;.概念 &#xff08;2&#xff09;.fork的写时拷贝 &#xff08;3&#xff09;.fork常规用法 2.进程终止 &#xff08;1&#xff09;.进程退出场景/退出码 &#xff08;2&#xff09;.进程常见退出方法 1&#xff09;.exit函数 …

类与对象(中级)

目录 1. 包 1.1 包的概念 1.2 导入包中的类 1.3 自定义包 1.4 常见的包 2. Java三大特性 -- 封装 3. Java三大特性 -- 继承 3.1 继承的概念&#xff1a; 3.2 继承的语法 3.3 父类成员访问 3.3.1 子类中访问父类的成员变量 3.3.2 子类中访问父类的成员方法 4. supe…

数据挖掘(六) 层次聚类

数据挖掘&#xff08;六&#xff09; 层次聚类 1.层次聚类简介 层次聚类算法(Hierarchical Clustering)将数据集划分为一层一层的clusters&#xff0c;后面一层生成的clusters基于前面一层的结果。层次聚类算法一般分为两类&#xff1a; Divisive 层次聚类&#xff1a;又称自…

【nacos】5.1 spring cloud + Nacos 实现统一配置管理

1. 解决的问题&#xff1a; 配置动态更新配置集中式管理配置内容的安全性和权限不同部署环境下的配置 2. 环境&#xff1a; ideaspring cloudspring-cloud-alibaba nacosmavenmqtt &#xff08;客户端&#xff0c;服务器采用的是EMQ X 5.0 &#xff09; 3. pom依赖 3.1 父级…