18 优先级队列

news2024/11/16 15:30:44

priority_queue介绍

1.优先级队列是一种容器适配器,根据弱排序标准,它的第一个元素总是最大的
2.此上下文类似于堆,堆中可以随时插入元素,检索最大堆元素
3.优先队列实现为容器适配器,容器适配器即将特定容器类封装作为底层容器类,queue提供一组特定的成员函数访问其元素,元素从特定容器的“尾部”弹出,称为优先级队列的顶部
4.底层容器可以是任何标准容器类的模板,也可以是特定设计的容器类,容器应该可以通过随机访问迭代器访问,并支持以下操作:

  • empty (): 检测是否为空
  • size ():返回有效元素个数
  • top (): 返回第一个元素的引用
  • push_back (): 在容器尾部插入元素
  • pop_back (): 删除容器尾部元素
    5.标准容器类vector和deque满足这些要求,如果没有初始化容器,默认使用vector
    6.需要支持随机访问迭代器,以便始终保持内部结构,容器适配器需要时自动调用算法函数make_heap, push_heap 和 pop_heap完成操作

使用

函数声明接口说明
priority ()/priotirt queue (first, last)构造一个空队列
empty ()检测是否为空
top ()返回队列中堆顶的元素
push ()插入元素
pop ()删除堆顶元素

默认情况下是大堆

#include <vector>
#include <queue>
#include <functional> // greater算法的头文件
void TestPriorityQueue()
{
 // 默认情况下,创建的是大堆,其底层按照小于号比较
 vector<int> v{3,2,7,6,0,4,1,9,8,5};
 priority_queue<int> q1;
 for (auto& e : v)
 q1.push(e);
 cout << q1.top() << endl;
 // 如果要创建小堆,将第三个模板参数换成greater比较方式
 priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());
 cout << q2.top() << endl;
}

练习

数组第k大元素

在这里插入图片描述

解析

将数组排序返回倒数第k个就可以完成,但时间复杂度不够。可以用优先级队列,将数组里的元素插入队列,弹k-1次队列,堆顶就是第k大的元素

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int> que;
        for (auto ch : nums) {
            que.push(ch);
        }

        while (--k) {
            que.pop();
        }

        return que.top();
    }
};

这种解法时间复杂度是O(k*logN + N),如果N远大于k,复杂度也会变高,可以优化一下,只用前k个元素建堆,比较数组剩下元素,更大的进堆,这样空间和效率都会好很多

先建立一个小堆的优先队列,前k个元素。从数组第k个元素开始遍历,比堆顶大就替换进去,先出堆顶再入堆。这样堆里就是整个数组前k大的元素,第k大的就是堆顶的元素

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int, vector<int>, greater<int>> que(nums.begin(),
                                                           nums.begin() + k);
        for (int i = k; i < nums.size(); i++) {
            if (nums[i] > que.top()) {
                que.pop();
                que.push(nums[i]);
            }
        }

        return que.top();
    }
};

实现

既然是容器适配器,成员函数就是模板的容器,调用对应的功能。结构是堆,在插入调用容器的插入功能后,要向上调整堆。删除堆顶元素后,要向下调整堆,保证堆顶元素时最值。看看标准库需要什么模板
在这里插入图片描述

首先是变量的类型,容器的种类,最后是一个仿函数

仿函数

仿函数的本质是一个类,它重载了函数调用符 () ,当这个类的对象使用()时,就调用了自己写的函数

优先队列需要两个仿函数,是大小比较的,一个从小到大,一个从大到小。传入模板T类型返回比较结果

	template <typename T>
	struct less
	{
		bool operator()(const T& x1, const T& x2)
		{
			return x1 < x2;
		}
	};

有了仿函数,优先队列的类的写法和之前的栈这些一样,只需要调用容器对应的功能。调整堆的写法在数据结构展示过,过程基本差不多,这是大小比较这里用仿函数

在这里插入图片描述

代码

#pragma once

namespace my_queue
{
	template <typename T>
	struct less
	{
		bool operator()(const T& x1, const T& x2)
		{
			return x1 < x2;
		}
	};

	template <typename T>
	struct greater
	{
		bool operator()(const T& x1, const T& x2)
		{
			return x1 > x2;
		}
	};

	template <class T, class container = vector<T>, class compare = less<T>>
	class priority_queue
	{
	public:
		void adjust_up(int child)
		{
			compare com;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (com(_con[parent], _con[child]))
				{
					swap(_con[child], _con[parent]);
				}
				else
				{
					return;
				}

				child = parent;
				parent = (child - 1) / 2;
			}
		}

		void adjust_down(int parent)
		{
			compare com;
			int child = parent * 2 + 1;

			while (child < _con.size())
			{
				//需判断右孩子是否越界
				if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
				{
					child++;
				}

				if (com(_con[parent], _con[child]))
				{
					swap(_con[child], _con[parent]);
				}
				else
				{
					break;
				}

				parent = child;
				child = parent * 2 + 1;
			}
		}

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

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

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

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

		bool empty()
		{
			return _con.empty();
		}
	private:
		container _con;
	};
}

仿函数的用法

对于自定义类,这里的仿函数会调用类的比较的重载。而对于类的指针,仿函数不一定只能用默认的大小比较,也可以自己实现一个仿函数,传入优先队列,就会调用自己的比较功能

下面是一个日期类

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;
};

实现仿函数,传入优先队列,就可以自己实现比较功能

//仿函数
struct __date_less
{
	bool operator()(const Date* d1, const Date* d2)
	{
		return *d1 < *d2;
	}
};
//传入
priority_queue <Date*, vector<Date*>, __date_less> que;

que.push(new Date(2018, 10, 29));
que.push(new Date(2018, 10, 28));
que.push(new Date(2018, 10, 30));
cout << *(que.top()) << endl;

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

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

相关文章

【源码阅读】evmⅠ

代码位置如下&#xff1a; 参考link 以太坊中有一个很重要的用途是智能合约&#xff0c;而其中evm模块是实现了执行智能合约的虚拟机。evm可以逐条解析执行智能合约的指令。 evm中的核心对象是EVM&#xff0c;代表一个以太坊虚拟机。其内部主要依赖&#xff1a;解释器Interore…

水下蓝牙耳机哪个牌子好?推荐四款高人气力作游泳耳机

在这个充满活力的时代&#xff0c;人们对于生活的追求早已不仅仅局限于日常的琐碎&#xff0c;更多的是对健康、对自我挑战的向往。运动&#xff0c;成为了现代人生活中不可或缺的一部分。而游泳&#xff0c;作为一项既能锻炼全身&#xff0c;又能享受水中美妙的运动&#xff0…

行尾检测论文汇总

文章目录 2023GNSS-Free End-of-Row Detection and Headland Maneuvering for Orchard Navigation Using a Depth Camera 2023 GNSS-Free End-of-Row Detection and Headland Maneuvering for Orchard Navigation Using a Depth Camera 摘要&#xff1a; 果园中基于GPS的导航…

苍穹外卖-day08:导入地址簿功能代码(单表crud)、用户下单(业务逻辑)、订单支付(业务逻辑,cpolar软件)

苍穹外卖-day08 课程内容 导入地址簿功能代码用户下单订单支付 功能实现&#xff1a;用户下单、订单支付 用户下单效果图&#xff1a; 订单支付效果图&#xff1a; 1. 导入地址簿功能代码&#xff08;单表crud&#xff09; 1.1 需求分析和设计 1.1.1 产品原型&#xff08…

Linux——进程通信(二) 匿名管道的应用:进程池

前言 之前我们学习了进程通过匿名管道进行通信&#xff0c;实现了两个进程的数据传输。 如果我们管理的是很多个进程&#xff0c;通过管道发送指令&#xff0c;因为如果管道中没有数据&#xff0c;读端必须等待&#xff0c;也就是被管理的进程们都在等待我发送的指令&#xf…

DevExpress WinForms crack,DevExpress WinForms组件套件和库

DevExpress WinForms crack,DevExpress WinForms组件套件和库 Reporting & Analytics - Reports, Pivot Tables, PDF Viewer. The DevExpress WinForms Subscription includes royalty-free user interface components for next-gen decision support systems. Whether you…

SegFormer 项目排坑记录

SegFormer 项目排坑记录 任务记录创建conda环境 准备数据库和预训练参数程序配置修改测试可视化训练 任务 需要复现SegFormer分割项目&#xff0c;似乎还有点麻烦&#xff0c;参考这几个进行复现&#xff0c;记录下过程&#xff1a; SegFormer mmsegmentation CSDN博客 知乎博…

保姆级教学!微信小程序设计全攻略!

微信小程序开启了互联网软件的新使用模式。在各种微信小程序争相抢占流量的同时&#xff0c;如何设计微信小程序&#xff1f;让用户感到舒适是设计师在产品设计初期应该考虑的问题。那么如何做好微信小程序的设计呢&#xff1f;即时设计总结了以下设计指南&#xff0c;希望对准…

车企都卷入鸿蒙开发的行了!难怪岗位需求量涨这么快!!!

近日华为鸿蒙 OS 官微宣布&#xff0c;首批汽车行业伙伴广汽传祺、岚图汽车、零跑汽车、凯翼汽车官宣加入鸿蒙生态。 这四家车企加入&#xff0c;会不会影响着其他车企想哪些手机APP厂商那种陆续的跟上呢&#xff1f; 其实在此前国内新能源扛把子比亚迪早已与华为达成了合作&a…

纳米阱可提高未来光学设备的光强度

一种配置光捕获器件的方法有望通过放大光和提高发光纳米材料的发射效率来获得更好的光学纳米器件&#xff0c;而无需复杂的技术升级。 强光束对于从医学到电子产品的无数应用至关重要&#xff0c;但使用日常光源生产它们具有挑战性。然而&#xff0c;它们可以由激光产生。激光…

校园暴力谁之过?如何正确引导孩子行为?

近日&#xff0c;关于河北邯郸校园霸凌事件持续发酵&#xff0c;很多媒体包括央视都是密切关注此事的发展动向&#xff0c;很多媒体人也在持续报道后续事态。那么对于14岁的的少年来说&#xff0c;花样年华的年纪&#xff0c;究竟为什么会犯下如此大的罪行&#xff1f;孩子为什…

电脑msvcp140_1.dll丢失的解决方法,总结5种可靠的方法

在日常使用电脑的过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“msvcp1401.dll丢失”。这个DLL文件是Microsoft Visual C Redistributable Package的一部分&#xff0c;对于许多基于Windows的应用程序来说至关重要。这个错误通常会导致某些应用程序无…

系统渐渐沦为“屎山”,这就是真相!

分享是最有效的学习方式。 博客&#xff1a;https://blog.ktdaddy.com/ 背景 小猫维护现有的系统也有一段时间了&#xff0c;踩坑也不少&#xff0c;事故不少。感兴趣的小伙伴可以了解一下&#xff0c;往期的小猫踩坑记合集。 这天&#xff0c;小猫找到了商城系统的第一任开发…

【数据结构与算法】:非递归实现快速排序、归并排序

&#x1f525;个人主页&#xff1a; Quitecoder &#x1f525;专栏&#xff1a;数据结构与算法 上篇文章我们详细讲解了递归版本的快速排序&#xff0c;本篇我们来探究非递归实现快速排序和归并排序 目录 1.非递归实现快速排序1.1 提取单趟排序1.2 用栈实现的具体思路1.3 代码…

掘根宝典之C++RTTI和类型转换运算符

什么是RTTI RTTI是运行阶段类型识别的简称。 哪些是RTTI? C有3个支持RTTI的元素。 1.dynamic_cast运算符将使用一个指向基类的指针来生成一个指向派生类的指针&#xff0c;否则该运算符返回0——空指针。 2.typeid运算符返回一个指出对象类型的信息 3.type_info结构存储…

【鸿蒙HarmonyOS开发笔记】如何使用图片插帧将低像素图片清晰放大

开发UI时&#xff0c;当我们的原图分辨率较低并且需要放大显示时&#xff0c;图片会模糊并出现锯齿。如下图所示 这时可以使用interpolation()方法对图片进行插值&#xff0c;使图片显示得更清晰。该方法的参数为ImageInterpolation枚举类型&#xff0c;可选的值有: ImageInte…

通过点击按钮实现查看全屏和退出全屏的效果

动态效果如图&#xff1a; 可以通过点击按钮&#xff0c;或者esc键实现全屏和退出全屏的效果 实现代码&#xff1a; <template><div class"hello"><el-button click"fullScreen()" v-if"!isFullscreen">查看全屏</el-butt…

centos创建并运行一个redis容器 并支持数据持久化

步骤 : 创建redis容器命令 docker run --name mr -p 6379:6379 -d redis redis-server --appendonly yes 进入容器 : docker exec -it mr bash 链接redis : redis-cli 查看数据 : keys * 存入一个数据 : set num 666 获取数据 : get num 退出客户端 : exit 再退…

猫头虎分享已解决Bug || TypeError: Cannot interpret ‘float‘ value as integer.

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

luceda ipkiss教程 62:等长波导布线(二)

教程 27介绍了两段波导等长布线的例子&#xff0c;下面同样是通过控制偏移量实现三段波导的等长布线&#xff1a; 所有代码如下&#xff1a; from si_fab import all as pdk from ipkiss3 import all as i3class demo(i3.Circuit):mmi i3.ChildCellProperty(doc"mmi in…