priority_queue的使用以及模拟实现

news2024/11/29 15:04:12

前言

上一期我们对stack和queue进行了使用的介绍,以及对底层的模拟实现!以及容器适配器做了介绍,本期我们在来介绍一个容器适配器priority_queue!

本期内容介绍

priority_queue的使用

仿函数介绍

priority_queue的模拟实现

什么是priority_queue?

priority_queue称为优先级队列,实际上就是堆(如果忘了什么是堆, 请点击这里)!它也是容器适配器,底层默认的容器是vector,默认是大堆!

常用的接口介绍

empty

判断是否为空

size

元素的个数

top

获取堆顶元素

注意:堆顶元素是不允许修改的,如果修改了会影响整个堆的结构,所以用const修饰!!

push

插入一个新元素

pop

删除堆顶的元素

OK,用一下!

void test()
{
	priority_queue<int> pq;
	pq.push(2);
	pq.push(15);
	pq.push(-1);
	pq.push(90);

	size_t sz = pq.size();
	cout << sz << endl;

	bool empty = pq.empty();
	cout << empty << endl;

	int top = pq.top();
	cout << top << endl;

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

priority_queue在是很有用的,例如TopK问题和堆排序。堆排序不在介绍,在数据结构已经详细的介绍了,这里拿个题目来再次理解一下TopK:

数组中第K大元素

它的题目意思就是让你,找出第K大的元素,但是要求时间度杂度O(N)!

思路:这里有三种, 第三种最优。

1、利用排序数组,然后返回倒数k个元素即可!

2、借助堆

3、快速选择算法(算法专栏介绍)

我们这里就先介绍前两种!

1、利用排序数组,然后返回倒数k个元素即可!(时间复杂度不符合)

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        return nums[nums.size() -k ];
    }
};

这里虽然过了,但是这个代码的时间复杂度是O(N*logN),不符合!

2、借助堆

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) 
    {
        priority_queue<int> pq(nums.begin(), nums.end());//将数组的元素放到堆里面
        for(int i = k; i > 1; i--)//将前k-1个弹出堆
        {
            pq.pop();
        }

        return pq.top();//返回堆顶的元素
    }
};

OK,这就过了,但是时间复杂度也是不行的!也还是O(N*logN)。最优解在后续的算法专栏会介绍!这主要是主要是演示一下堆在OJ中的用法~!

仿函数介绍

仿函数是一种类,它的对象可以向函数一样被调用。他是一种可调用对象,可以向像函数一样接收参数并返回结果!通常情况,仿函数是通过一个类实现operator ()来实现的!

 例如,上面刚介绍的priority_queue:

这里的less就是一个仿函数!还有就是sort:

OK,举个仿函数的例子:

struct cmp
{
	bool operator()(const int& a, const int& b)
	{
		return a < b;
	}
};

//class cmp
//{
//public:
//	bool operator()(const int& a, const int& b)
//	{
//		return a < b;
//	}
//};

void test()
{
	cmp cm;
	int result1 = cm(3, 5);

	int result2 = cm.operator()(3, 5);

	int result3 = cmp()(3, 5);
}

第一种和第二种本质是同一种,第二种是第一种的显示调用,第三种是匿名对象调用!当然这里的struct可以是class但注意的是如果是class你必须吧权限设置为public!

仿函数的用途

仿函数的用途我目前碰到的有两种(后面遇见了在补充):

1、STL算法库中的某些算法来用 仿函数定义他们的行为!例如:sort等

2、容器的自定义行为!例如priority_queue指定大小堆!等

#include <algorithm>
template<class T>
struct Compare
{
	bool operator() (const T& a, const T& b)
	{
		return a > b;
	}
};

void test()
{	
	vector<int> v = { 1,3,0,12,43,5,9 };
	sort(v.begin(), v.end());//默认是升序
	for (const auto& e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	sort(v.begin(), v.end(), Compare<int>());//降序  --》用匿名对象
	Compare<int> cmp;
	sort(v.begin(), v.end(), cmp);//降序 --》用实名对象
	for (const auto& e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

大堆和小堆

void test()
{
	vector<int> v = { 0,12,43,5,9 };
	priority_queue<int> pq1(v.begin(), v.end());//默认大堆
	while (!pq1.empty())
	{
		cout << pq1.top() << " ";
		pq1.pop();
	}
	cout << endl;

	priority_queue<int, vector<int>, greater<int>> pq2(v.begin(), v.end());//指定为小堆
	while (!pq2.empty())
	{
		cout << pq2.top() << " ";
		pq2.pop();
	}
	cout << endl;
}

这里priority_queue默认是less:

template <class T, class Container = vector<T>, class Compare = less<typename Container::value_type> > class priority_queue;

如果要切换为小堆,就得指定仿函数为greater,但是我们知道参数是倒着(从右往左)传递的,所以这里要指定为小堆的话,还要指定它的底层适配的容器~!一般是vector也可以是deque!!

priority_queue的模拟实现

priority_queue() 
{}

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

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

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

这几个不在多说,很简单直接调用默认容器的相关接口即可~!主要介绍一下这里的插入、删除和用用一段迭代器区间构造!

push

先插入到适配容器的尾部,然后进行向上调整!(向上调整不了解的点击上面介绍对那个文章的链接回忆一下)

另外注意的是这里我们不再是以前的大于小于比较了,而是用仿函数!

void adjust_up(size_t child)
{
	size_t parent = (child - 1) / 2;
	while (child > 0)
	{
		if (cmp()(_con[parent], _con[child]))//孩子比父亲大(小),交换
		{
			swap(_con[parent], _con[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;//否则,结束掉
		}
	}
}
void push(const T& val)
{
	_con.push_back(val);
	adjust_up(_con.size() - 1);
}

pop

先交换堆顶数据和最后一个数据!然后删除掉,堆顶数据,进行向下调整~!(向下调整和向上调整一样,详细见数据结构那里)

另外注意的是这里我们不再是以前的大于小于比较了,而是用仿函数!

void adjust_down(size_t parent)
{
	size_t child = parent * 2 + 1;//假设第一个孩子就是要交换的孩子
	while (child < _con.size())
	{
		if (child + 1 < _con.size() && cmp()(_con[child], _con[child + 1]))//假设错了
		{
			++child;//调整
		}

		if (cmp()(_con[parent], _con[child]))//孩子比父亲大(小)
		{
			swap(_con[parent], _con[child]);//交换
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;//否则,结束掉
		}
	}
}
void pop()
{
	swap(_con.front(), _con.back());
	_con.pop_back();
	adjust_down(0);
}

迭代器区间构造

这里其实注意的就一点,当把数据给到适配器的容器后,要保证是堆结构!所以就要建堆,建堆的方式有两种(详见数据结构那里)向上调整建堆 和 向下调整建堆!

template<class Iterator>
priority_queue(Iterator first, Iterator last)
	:_con(first, last)
{
	int sz = _con.size();
	//向下调整建堆 O(N)
	for (int i = (sz - 1) / 2; i >= 0; i--)
	{
		adjust_down(i);
	}

	//向下调整建堆 O(N*logN)
	//for (int i = 1; i < sz; i++)
	//{
	//	adjust_up(i);
	//}
}

全部源码

#pragma once

#include <vector>

namespace cp
{
	template<class T>
	struct less
	{
		bool operator()(const T& a, const T& b)
		{
			return a < b;
		}
	};

	template<class T>
	struct greater
	{
		bool operator()(const T& a, const T& b)
		{
			return a > b;
		}
	};


	template<class T, class Con = std::vector<T>, class cmp =  less<T>>
	class priority_queue
	{
	public:
		priority_queue() 
		{}

		template<class Iterator>
		priority_queue(Iterator first, Iterator last)
			:_con(first, last)
		{
			int sz = _con.size();
			//向下调整建堆 O(N)
			for (int i = (sz - 1) / 2; i >= 0; i--)
			{
				adjust_down(i);
			}

			//向下调整建堆 O(N*logN)
			//for (int i = 1; i < sz; i++)
			//{
			//	adjust_up(i);
			//}
		}

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

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

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

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

		void pop()
		{
			swap(_con.front(), _con.back());
			_con.pop_back();
			adjust_down(0);
		}

	private:
		void adjust_up(size_t child)
		{
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
				if (cmp()(_con[parent], _con[child]))//孩子比父亲大(小),交换
				{
					swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;//否则,结束掉
				}
			}
		}

		void adjust_down(size_t parent)
		{
			size_t child = parent * 2 + 1;//假设第一个孩子就是要交换的孩子
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && cmp()(_con[child], _con[child + 1]))//假设错了
				{
					++child;//调整
				}

				if (cmp()(_con[parent], _con[child]))//孩子比父亲大(小)
				{
					swap(_con[parent], _con[child]);//交换
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;//否则,结束掉
				}
			}
		}

	private:
		Con _con;
	};
}

OK,验证一下:

OK,没有问题!本期内容就分享到这里,好兄弟我们下期再见!

结束语:无人问津的日子,更应该加倍努力!

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

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

相关文章

2024年3月文章一览

2024年3月编程人总共更新了12篇文章&#xff1a; 1.2024年2月文章一览 2.Programming Abstractions in C阅读笔记&#xff1a;p308-p311 3.Programming Abstractions in C阅读笔记&#xff1a;p312-p326 4.Programming Abstractions in C阅读笔记&#xff1a;p327-p330 5.…

更改el-cascade默认的value和label的键值

后端返回的树结构中&#xff0c;label的key不是el-cascade默认的label&#xff0c;我需要改成对应的字段&#xff0c;但是一直没有成功&#xff0c;我也在文档中找到了说明&#xff0c;但是我没注意这是在props中改&#xff0c;导致一直不成功 这是我一开始错误的写法&#xf…

超越ChatGPT,国内快速访问的强大 AI 工具 Claude

claude 3 opus面世后&#xff0c;网上盛传吊打了GPT-4。网上这几天也已经有了许多应用&#xff0c;但竟然还有很多小伙伴不知道国内怎么用gpt&#xff0c;也不知道怎么去用这个据说已经吊打了gpt-4的claude3。 今天我们想要进行的一项尝试就是—— 用claude3和gpt4&#xff0c…

互联网大厂ssp面经(操作系统:part1)

1. 什么是进程和线程&#xff1f;它们之间有什么区别&#xff1f; a. 进程是操作系统中运行的一个程序实例。它拥有独立的地址空间和资源&#xff0c;可以独立执行。 b. 线程是进程内的一个执行单元&#xff0c;一个进程可以包含多个线程。 c. 线程共享进程的资源&#xff0c;…

liunx系统发布.net core项目

liunx系统发布.net core项目 准备.net6程序运行环境部署nginx&#xff0c;通过一个地址既能访问web api&#xff0c;又能访问web项目有一个客户把web api放到docker中&#xff0c;想通过nginx转发&#xff0c;nginx也支持配置多个程序api接口的其它 liunx系统&#xff1a;cento…

程序员生产力工具推荐

1.SSH客户端 XTerminal Xterminal - 更好用的开发工具&#xff0c;但不止于(SSH/控制台/More) 有着比XShell好看的多的界面&#xff0c;免费版使用起来绰绰有余。 2.文件内容搜索工具 FileLocator FileLocator Pro 专业全文检索工具文件搜索软件丨中文网站特价购买 everyth…

Excel 记录单 快速录入数据

一. 调出记录单 ⏹记录单功能默认是隐藏的&#xff0c;通过如下如图所示的方式&#xff0c;将记录单功能显示出来。 二. 录入数据 ⏹先在表格中录入一行数据&#xff0c;给记录单一个参考 ⏹将光标至于表格右上角&#xff0c;然后点击记录单按钮&#xff0c;调出记录单 然后点…

Terraform 状态不同步处理

背景 在使用 Terraform 创建 TencentCloud TKE 的时候&#xff0c;手贱把 node pool 删掉了。导致执行 destroy, plan 都会报错。 │ Error: [TencentCloudSDKError] CodeInternalError.UnexpectedInternal, Messagerelated node pool query err(get node pool failed: [E501…

kaggle 泰坦尼克号1(根据男女性存活率)

kaggle竞赛 泰坦尼克号 流程 下载kaggle数据集导入所要使用的包引入kaggle的数据集csv文件查看数据集的大小和长度去除冗余数据建立特征工程导出结果csv文件 1.下载kaggle数据集 2.导入所要使用的包 import pandas as pd import numpy as np import matplotlib.pyplot as …

2024.4.12蚂蚁庄园今日答案:豆腐在烹调时容易碎有什么办法可以避免?

原文来源&#xff1a;蚂蚁庄园今日答案 - 词令 蚂蚁庄园是一款爱心公益游戏&#xff0c;用户可以通过喂养小鸡&#xff0c;产生鸡蛋&#xff0c;并通过捐赠鸡蛋参与公益项目。用户每日完成答题就可以领取鸡饲料&#xff0c;使用鸡饲料喂鸡之后&#xff0c;会可以获得鸡蛋&…

构建高效网络:深入理解正向与反向代理的作用与配置

正向代理 如果把局域网外的互联网环境想象成一个巨大的资源库&#xff0c;则局域网中的客户端要访问互联网则需要通过代理服务器来访问&#xff0c;这种代理成为正向代理。 示例&#xff1a; 用户想要访问 https://chensir.ink &#xff08;目标服务器&#xff09;&#xff0…

vivado 在硬件中调试逻辑设计

在硬件中调试逻辑设计 设计中包含调试核后 &#xff0c; 您可使用运行时间逻辑分析器功能来对硬件中的设计进行调试。 使用 Vivado Logic Analyzer 进行设计调试 Vivado Logic Analyzer 功能可用于与设计中运行的新 ILA 、 VIO 和 JTAG-to-AXI Master 调试核进行交互。…

02 Windows操作系统密钥激活流程

Windows系统的激活流程通常包括以下步骤&#xff1a; AI步骤 1. 购买正版产品密钥&#xff1a;在正式激活Windows系统之前&#xff0c;你需要购买一个合法的产品密钥。你可以通过Microsoft官方网站或授权的零售商购买密钥。 2. 输入产品密钥&#xff1a;在购买后&#xff0c;你…

1.8V LDO电路 ➕1.2V bandgap电路

1.8V LDO电路 ➕1.2V bandgap电路&#xff08;WX:didadidadidida313&#xff0c;加我备注&#xff1a;CSDN LDO&#xff0c;谢绝白嫖哈&#xff09; 1.8V LDO电路 ➕1.2V bandgap电路,基于tsmc180nm工艺库 带设计仿真报告&#xff0c;非常适合新手入门&#xff01; 本文采用…

【智能算法应用】灰狼算法求解TSP问题

目录 1.算法原理2.TSP数学模型3.结果展示4.参考文献 1.算法原理 【智能算法】灰狼算法&#xff08;GWO&#xff09;原理及实现 2.TSP数学模型 旅行商问题&#xff08;TSP&#xff09;是一种著名的组合优化问题&#xff0c;它涉及寻找给定一组城市及其之间的距离或成本&#…

室内定位中文综述阅读

1 室内高精度定位技术总结与展望 [4]柳景斌,赵智博,胡宁松等.室内高精度定位技术总结与展望[J].武汉大学学报(信息科学 版),2022,47(07):997-1008.DOI:10.13203/j.whugis20220029. 1.1.1 WiFi‐RTT定位 2016 年 12 月&#xff0c;随着新版 IEEE802.11 标准的公布&#xff0c…

逆向案例二十一——遇到混淆怎么办

开始新的板块尝试&#xff0c;混淆了怎么办 网址&#xff1a;极简壁纸_海量电脑桌面壁纸美图_4K超高清_最潮壁纸网站 抓包抓到&#xff0c;好久没做解密了&#xff0c;奥里给干他&#xff01;&#xff1a; 搜索关键字&#xff0c;打上断点&#xff0c;点击第二页。 _0x10a345…

中国企业级存储市场:五年来首次负增长,第二曲线在哪里?

出人意料&#xff0c;中国企业级存储市场出现过去五年来的首次负增长。 IDC最新《中国企业级存储市场跟踪报告,2023》显示&#xff0c;2023年中国企业级存储市场规模达到66亿美元&#xff0c;同比下降0.6%。外部环境的动荡与不确定性的陡增&#xff0c;让中国不少行业用户受到…

深度学习每周学习总结P4(猴痘识别)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 –来自百度网盘超级会员V5的分享 目录 0. 总结1. 数据导入部分2. 划分数据集3. 模型构建部分3.1 模型构建3.2 公式推导 4. 设置超参数5. …

关于AI Agent、RAG技术揭秘:如何让人工智能更懂你?

人工智能技术正以前所未有的速度改变着我们的世界。从深度学习算法的突破到自动化和机器学习技术的进步。在这个变革的时代&#xff0c;几种前沿技术尤其引人注目&#xff0c;其中包括RAG&#xff08;Retrieval-Augmented Generation&#xff09;、AI Agent以及多模态技术。 近…