【C++杂货铺】优先级队列的使用指南与模拟实现

news2025/1/22 12:38:40

在这里插入图片描述

文章目录

  • 一、priority_queue的介绍
  • 二、priority_queue的使用
    • 2.1 数组中的第k个最大元素
  • 三、priority_queue模拟实现
    • 3.1 仿函数
    • 3.2 成员变量
    • 3.3 成员函数
      • 3.3.1 构造函数
      • 3.3.2 AdjustDown
      • 3.3.3 push
      • 3.3.4 AdjustUp
      • 3.3.5 pop
      • 3.3.6 empty
      • 3.3.7 size
  • 四、结语

一、priority_queue的介绍

  • 优先级队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。

  • 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先级队列中位于顶部的元素)。

  • 优先级队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue 提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先级队列的顶部。

  • 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问、迭代器访问,并支持以下操作:

    • empty():检测容器是否为空

    • size():返回容器中有效元素的个数

    • front():返回容器中第一个元素的引用

    • push_back():在容器尾部插入元素

    • pop_back():删除容器尾部元素

  • 标准容器类 vector 和 deque 满足这些需求。默认情况下,如果没有为特定的 priority_queue 类实例化指定容器类,则使用 vector。

  • 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数 make_heap、push_heap 和 pop_heap 来自动完成次操作。

二、priority_queue的使用

优先级队列默认使用 vector 作为其底层存储数据的容器,在 vector 上又使用了堆算法将 vector 中元素构造成堆的结构,因此 priority_queue 就是堆,所有需要用到堆的位置,都可以考虑使用 priority_queue。注意:默认情况下 priority_queue 是大堆。

函数声明接口说明
priority_queue()构造一个空的优先级队列
empty()检测优先级队列是否为空,是返回 true,否则返回 false
top()返回优先级队列中最大(最小)元素,即堆顶元素
push(x)在优先级队列中插入元素 x
pop()删除优先级队列中最大(最下)元素,即堆顶元素

小Tips:默认情况下,priority_queue 是大堆(默认第三个模板参数是 less);如果在 priority_queue 中放自定义类型的数据,用户需要在自定义类型中提供 > 或者 < 的重载。

2.1 数组中的第k个最大元素

在这里插入图片描述

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) 
    {
        //建一个大堆,向下调整建堆的时间复杂度是O(N)
        priority_queue<int> pq(nums.begin(), nums.end());

        //pop k-1次,时间复杂度是O(K*logN)
        while(--k)
        {
            pq.pop();
        }

        return pq.top();
    }
};

上面这种做法当 K 的值接近 N 的时候,它的时间复杂度就是 O(N*logN),是不满足题目要求的,下面再用另外一种方法来解决。

lass Solution {
public:
    int findKthLargest(vector<int>& nums, int k) 
    {
        //用前k个数建一个小堆
        priority_queue<int, vector<int>, greater<int>> pq(nums.begin(), nums.begin() + k);

        //遍历剩下的 N-k 个,比堆顶大就换进去
        //时间复杂度是 (N-K)logN
        for(size_t i = k; i < nums.size(); i++)
        {
            if(nums[i] > pq.top())
            {
                pq.pop();
                pq.push(nums[i]);
            }
        }
        return pq.top();
    }
};

上面这种解法是先用数组中的前 K 个元素建一个小堆,然后从数组的第 K+1 个元素开始往后遍历,遇到比堆顶元素大的就将堆顶的元素 pop 出来,将当前元素 push 进去。建堆的时间复杂度是 O(K),更新小堆的时间复杂度是 O((N-K)logK),这种做法当 K 很小的时候时间复杂度可以近似看做 O(NlogK),当 K 很大的时候,时间复杂度可以近似看做 O(logK)。

三、priority_queue模拟实现

3.1 仿函数

仿函数本质上一个类,之所以把它叫仿函数是因为这个类的对象可以像函数一样去使用。举例如下:

//一个仿函数
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;//该类型对象可以像函数一样去使用
	//cout << lessfunc.operator()(1, 2) << endl;//和上面等价
	return 0;
}

小Tips:仿函数一般都是类模板,这样就可以支持不同类型的大小比较,前提是该种类型重载实现了比较运算符。仿函数的诞生是为了代替函数指针,函数指针的可读性比较差。

3.2 成员变量

template<class T, class Container=std::vector<T>, class Compare = Less<T>>
	class priority_queue
	{
	public:
		//成员
	private:
		Container _con;
	};

小Tips:需要注意这里有三个模板参数,第一个模板参数表示要在优先级队列中存储的数据类型;优先级队列本质上也是一个容器适配器,所以第二个模板参数表示优先级队列要使用的底层容器;第三个模板参数是一个仿函数,用来进行大小比较的,因为优先级队列中会涉及到建大堆还是建小堆,中间会涉及到比较,如果没有这个仿函数,那么大小比较就只能写死了,这样不太好。

3.3 成员函数

3.3.1 构造函数

priority_queue()
{}

template<class InputeIterator>
priority_queue(InputeIterator first, InputeIterator last)
{
	//先将数据插入
	while (first != last)
	{
		_con.push_back(*first);
		++first;
	}

	//建堆,从最后一个非叶子节点开始向下调整
	for (int i = (_con.size() - 1) / 2; i >= 0; i--)
	{
		AdjustDown(i);
	}
}

小Tips:迭代器区间构造函数是一个函数模板,只要是能支持 ++ 操作的迭代器区间都可以,即单向迭代器、双向迭代器、随机迭代都可以。其次将数据插入容器后还需要建堆,这里采用向下调整建堆,它的时间复杂度是 O(N)。

3.3.2 AdjustDown

void AdjustDown(int parent)
{
	Compare com;
	while (parent * 2 + 1 < (int)_con.size())
	{
		//找到最大的孩子
		int maxchild = parent * 2 + 1;
		if (maxchild + 1 < (int)_con.size() && com(_con[maxchild], _con[maxchild + 1]))
		{
			maxchild++;
		}

		//和父节点比较
		if (com(_con[parent], _con[maxchild]))
		{
			std::swap(_con[maxchild], _con[parent]);
			parent = maxchild;
		}
		else
		{
			break;
		}
	}
}

小Tips:在仿函数的加持下,向下调整代码中的大小比较不再固定,建大堆和小堆这一份代码就够了,最终是大堆还是小堆是由仿函数来控制的。

3.3.3 push

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

小Tips:在优先级队列的尾部追加数据,会涉及到向上调整,向上调整的代码如下所示。

3.3.4 AdjustUp

void AdjustUp(int child)
{
	Compare com;
	int parent = (child - 1) / 2;
	while (child > 0)//父亲不会小于0,因此这里的判断条件要用孩子大于0,孩子等于0说明已经调整到根节点,就无需继续调整了
	{
		if (com(_con[parent], _con[child]))
		{
			std::swap(_con[child], _con[parent]);
			child = parent;
			parent = (child - 1) / 2;//这里parent不会小于0
		}
		else
		{
			break;
		}
	}
}

3.3.5 pop

void pop()
{
	std::swap(_con[0], _con[_con.size() - 1]);
	_con.pop_back();
	AdjustDown(0);
}

小Tips:优先级队列中出数据,出的是堆顶的数据,堆顶的数据也就是容器中的第一个数据,如果底层容器是 vector 那么堆顶的数据就是下标为 0 的数据,出堆顶的数据不能直接使用头删,这样会导致后面数据的父子关系全乱了。正确的做法是,将堆顶的数据和容器中的最后一个数据交换,然后执行 pop_back 去删除,最后还需要从根节点开始执行一次向下调整,让交换到堆顶的数据去到它应该去的地方。

3.3.6 empty

bool empty()
{
	return _con.size() == 0;
}

3.3.7 size

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

四、结语

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,春人的主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是春人前进的动力!

在这里插入图片描述

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

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

相关文章

浅谈应急照明及疏散指示系统在建筑物消防的应用

安科瑞 华楠 摘要&#xff1a;智能消防应急照明及琉散指示系统是建筑物消防系统中的重要内容,为人们的生命安全提供了重要的安全保证。该文在研究中主要以智能消防应急照明及琉散指示系统为要点,探究智能消防应急照明及琉散指示系统的特征,其核心目的是优化智能消防应急照明及…

无代码编程时代的到来:新兴工具和平台的前瞻展望

目录 一、无代码编程的概念和意义 二、新兴工具和平台的前瞻展望 2.1 低代码/无代码开发平台&#xff1a; 2.2 人工智能应用开发工具&#xff1a; 2.3 数据分析和可视化工具&#xff1a; 三、未来发展前景和挑战 随着技术的不断进步和发展&#xff0c;传统的编程模式面临…

2023,DaaS驶入“AI大航海时代”

2023&#xff0c;“制胜”已然成为所有行业、企业的共同命题&#xff0c;随着数字化行至中程&#xff0c;数据壁垒逐渐被打破&#xff0c;DaaS作为企业增长问题的解法&#xff0c;再次被看到。 作者|斗斗 编辑|皮爷 出品|产业家 2002年&#xff0c;在竞争激烈的美国职业…

掌握时间复杂度, 编写高效代码

White graces&#xff1a;个人主页 &#x1f649;专栏推荐:Java入门知识&#x1f649; &#x1f649; 内容推荐:巧用抽象类与接口&#xff0c;打造高效Java程序(下)&#x1f649; &#x1f439;今日诗词:昨夜星辰昨夜风&#xff0c;画楼西畔桂堂东&#x1f439; ⛳️点赞 ☀️…

【多线程】线程池 详解

线程池 详解 1. 线程池是什么2. 标准库中的线程池3. 实现线程池4. 面试题 1. 线程池是什么 虽然线程的创建和销毁的开销比较小, 但还是有的, 如果频繁的创建和销毁线程, 开销还是比较大的.解决: 线程池或者协程, 本文主讲线程池. 线程池: 把线程提前创建好, 放到池子里, 后面…

黑马 小兔鲜儿 uniapp 小程序开发- 分类模块- day04

黑马 小兔鲜儿 uniapp 小程序开发- 推荐模块- day03_软工菜鸡的博客-CSDN博客 本课程是全网首套用 vue3 加 TS 写的 uniapp 项目&#xff0c; 里面大量封装自己的组件库&#xff0c;课程从 uni-app 基础入手&#xff0c;按照9大电商业务模块逐步实现完整的电商购物流程业务&am…

技术人员应该使用那种搜索引擎?

built-in.o是Linux内核中的组件 下面是三种主流搜索引擎的搜索结果&#xff0c;请参考&#xff0c;一切尽在不言中。

CSS - 快速实现悬浮吸顶,当页面滑动一定距离时固定吸附在顶部(position: sticky)

效果图 如下图所示&#xff0c;利用 position: sticky 属性轻松实现。 示例代码 新建一个 *.html 文件&#xff0c;一键复制运行起来。 <body><section class"content"><div class"item">我是悬浮吸顶区域</div><h1>我是…

【leetcode 力扣刷题】栈和队列的基础知识 + 栈的经典应用—匹配

栈和队列的基础知识 栈的经典应用—匹配 栈和队列基础知识232. 用栈实现队列225. 用队列实现栈 20. 有效的括号1047. 删除字符串中的所有相邻重复项 栈和队列基础知识 数据结构课程介绍线性结构的时候&#xff0c;介绍有线性表、链表、栈和队列。线性表&#xff0c;比如array…

室内探索无人机,解决复杂环境下的任务挑战!

前言 室内探索无人机是一种专为在室内环境中进行任务的无人机系统。相比传统的人员部署&#xff0c;室内探索无人机具有更高的灵活性和机动性&#xff0c;能够在复杂的室内环境中执行任务&#xff0c;用于未知环境的探索和特定目标的搜索。 为完成无人机室内搜索与识别等复杂…

无缝数据传输:StreamSet安装部署的最佳实践

文章目录 概要安装方法尝试安装部署方案1. 下载datacollector镜像2. 启动容器3. 访问streamsets小结 概要 StreamSets是一款流数据集成平台&#xff0c;旨在帮助用户轻松地收集、处理和传输大规模数据流。它提供了直观的界面和强大的功能&#xff0c;可用于实时数据流的提取、…

无线测温系统在运行时怎么判断配电设备出现故障?

现如今&#xff0c;电力测温方面使用无线测温系统的使用范围越来越大&#xff0c;比较熟悉的人都了解&#xff0c;传统的温度测量方式周期长、施工复杂&#xff0c;效率低&#xff0c;不便于管理&#xff0c;发生故障时要耗费大量的人力物理排查和重新铺设线缆。而在特定场合下…

若依+lodop+jasperreports+ireport 设计打印票据格式(一)

若依lodopjasperreportsireport 设计打印票据格式 一、设计表格 官网下载ireport5.6&#xff0c;解压安装&#xff0c;jdk 1.7适配ireport5.6&#xff0c;jdk1.8不适配 安装好jdk1.7(不用配置path&#xff0c;安装好就行)&#xff0c;进入ireport5.6安装目录&#xff0c;找到…

异步FIFO设计的仿真与综合技术(2)

概述 本文主体翻译自C. E. Cummings and S. Design, “Simulation and Synthesis Techniques for Asynchronous FIFO Design 一文&#xff0c;添加了笔者的个人理解与注释&#xff0c;文中蓝色部分为笔者注或意译。前文链接&#xff1a;异步FIFO设计的仿真与综合技术&#xff0…

模态分析的概念。C++减振器设计。

一、说明 模态分析是工程和物理学中用于研究系统或结构动态特性的技术。它涉及分析系统的振动或振荡的自然模式以及相应的频率、阻尼系数和振型。 在模态分析中&#xff0c;所研究的系统通常表示为一组质量、刚度和阻尼元件&#xff08;在下面的文章中忽略了阻尼&#xff09;。…

ARTS 打卡 第一周,初试ARTS

前言 认识三掌柜的想必都知道&#xff0c;我持续创作技术博客已经有6年时间了&#xff0c;固定每个月发布不少于6篇博文。同时&#xff0c;自己作为一名热爱分享的开发者&#xff0c;像ARTS这样的活动自然少不了我。由于我是打算挤在一起分享&#xff0c;之前都是做了本地文档记…

大数据Flink(七十九):SQL 的容错(Checkpoint)

文章目录 SQL 的容错(Checkpoint) 一、Checkpoint介绍

华为云云耀云服务器L实例评测|云耀云服务器L实例部署SpaceHuggers网页小游戏

华为云云耀云服务器L实例评测&#xff5c;云云耀云服务器L实例部署SpaceHuggers网页小游戏 一、云耀云服务器L实例介绍1.1 云耀云服务器L实例简介1.2 云耀云服务器L实例特点 二、SpaceHuggers小游戏介绍2.1 SpaceHuggers简介2.2 SpaceHuggers游戏玩法 三、实践环境介绍3.1 本次…

在UOS/Deepin下安装 Python 3.11.5 图文详解

01 先把操作系统更新一下 在开始菜单中&#xff0c;找到“终端”&#xff0c;点击启动&#xff0c;并依次输入以下两条命令即可&#xff1a; sudo apt update sudo apt upgrade 特别说明&#xff1a;Uos/Deepin 系统&#xff0c;要先进入“开发者模式”才行。“ 设置 — 通用…

如何用Polygon ID来证明你不是机器人?

1. 引言 喜剧演员约翰穆拉尼在最近的一个单口相声特辑中说&#xff1a;“世界是由机器人管理的&#xff0c;我们一天中的大部分时间都在告诉他们&#xff0c;我们不是一个仅仅登录并查看自己东西的机器人。” 这种经历很常见&#xff0c;从乏味的&#xff08;“找到所有的停车…