priority_queue的使用以及模拟实现

前言

上一期我们对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.mzph.cn/news/808828.shtml

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

相关文章

自动驾驶规划方法综述

自动驾驶规划方法综述 附赠自动驾驶学习资料和量产经验&#xff1a;链接 最近看到一篇非常好的关于自动驾驶规划方法的综述&#xff08;A Review of Motion Planning Techniques for Automated Vehicles&#xff09;&#xff0c;写的非常好&#xff0c;总结了近几十年来总动驾…

YOLOv5实战记录06 Gradio搭建Web GUI

个人打卡&#xff0c;慎看。 指路大佬&#xff1a;【手把手带你实战YOLOv5-入门篇】YOLOv5 Gradio搭建Web GUI_哔哩哔哩_bilibili 先放一张效果图&#xff1a; 零、虚拟环境激活 之前up说要激活环境时&#xff0c;我没当回事儿&#xff0c;今天突然想&#xff0c;激活环境然后…

LangChain教程 | 实践过程报错集 | 持续更新

这是本人最近在做langchain教程过程中的遇到的报错&#xff0c;不分先后顺序。 报错&#xff1a;TypeError: NoneType object is not iterable 这个报错很常见&#xff0c;咱们要看原始报错的位置是哪里&#xff0c;下面是我的截图&#xff1a; 找到源头之后&#xff0c;就在源…

缝合的作品(并查集/逆序)

、思路&#xff1a;首先是并查集来做&#xff0c;首先给给每个单词一个id&#xff0c;然后把它放到ans[i]处。 对于操作1&#xff1a;把a单词换为单词b&#xff0c;就相当于a、b两个集合结合。然后再给a单词赋一个新的id&#xff0c;用来进行操作2&#xff0c;因为之后的操作2…

蓝桥杯-【二分】肖恩的苹果林

思路:有点类似于找最大值的最小化。 代码及解析 常规的模板引用40% #include <bits/stdc.h> using namespace std; #define ll long long const ll N1e53; ll a[N]; ll m,n; ll chack(ll mid) {int res1,last0;for(int i1;i<n;i){ if(a[i]-a[last]>mid){res;las…

DeepStream做对象模糊的几种方法

有时候&#xff0c;我们需要对视频的敏感信息做模糊处理&#xff0c;比如模糊人脸&#xff0c;车牌。 有时候&#xff0c;也需要对整帧做模糊&#xff0c;或者遮挡。比如这个例子。 下面介绍几种模糊的办法。 1. 通过nvosd deepstream-test1是DeepStream最简单的一个例子&…

代码随想录训练营day36

第八章 贪心算法 part05 1.LeetCode. 无重叠区间 1.1题目链接&#xff1a;435. 无重叠区间 文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;B站卡哥视频 1.2思路&#xff1a;我来按照右边界排序&#xff0c;从左向右记录非交叉区间的个数。最后用区间总数减去非交叉区…

Transformer - 注意⼒机制 Attention 中的 Q, K, V 解释(2)

Transformer - 注意⼒机制 Attention 中的 Q, K, V 解释&#xff08;2&#xff09; flyfish Transformer - 注意⼒机制 Scaled Dot-Product Attention 计算过程 Transformer - 注意⼒机制 代码实现 Transformer - 注意⼒机制 Scaled Dot-Product Attention不同的代码比较 Tran…

【数字化转型】上市公司智能制造词频统计数据(1991-2022年)

数据来源&#xff1a;上市公司年报 时间跨度&#xff1a;1991-2022年 数据范围&#xff1a;上市公司 数据指标&#xff1a; 版本一 智能制造 智能机器 智能生产 机器人 全自动 全机器 版本二 宏观政策 中国制造2025 工业4.0 互联网 范式特征 自动化 信息化 信息…

Socks5代理IP使用教程及常见使用问题

当我们在互联网上浏览网页、下载文件或者进行在线活动时&#xff0c;隐私和安全问题常常被提及。在这样的环境下&#xff0c;一个有效的解决方案是使用Sock5IP。本教程将向您介绍Sock5IP的使用方法&#xff0c;帮助您保护个人隐私并提升网络安全。 一、什么是Sock5IP&#xff1…

QT学习day5

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget),socket(new QTcpSocket(this)) {ui->setupUi(this);//初始化界面ui->msgEdit->setEnabled(false);//不可用ui->sendBtn-&g…

spring启动流程

【spring容器启动】之bean的实例化和初始化&#xff08;文末附:spring循环依赖原理&#xff09;_实例化bean和初始化bean-CSDN博客

js中new FormData对象的作用

一、js FormData方法介绍 1. 概述 FormData类型其实是在XMLHttpRequest 2级定义的&#xff0c;它是为序列化表以及创建与表单格式相同的数据&#xff08;当然是用于XHR传输&#xff09;提供便利。 2. 构造函数 创建一个formData对象实例有几种方式&#xff1a; 1、创建一个空…

[Kubernetes[K8S]集群:master主节点初始化]:通过Calico和Coredns网络插件方式安装

文章目录 操作流程&#xff1a;前置&#xff1a;Docker和K8S安装版本匹配查看0.1&#xff1a;安装指定docker版本 **[1 — 7] ** [ 配置K8S主从集群前置准备操作 ]一&#xff1a;主节点操作 查看主机域名->编辑域名->域名配置二&#xff1a;安装自动填充&#xff0c;虚拟…

如何借助AI高效完成写作提纲

AI变革力量&#xff1a;未来数据中心的智能化之旅&#xff01; 在当今这个信息爆炸的时代&#xff0c;人工智能&#xff08;AI&#xff09;在众多领域展现出了它的能力&#xff0c;特别是在写作领域。AI写作工具不仅能够帮助我们高效地生成内容&#xff0c;还能在一定程度上提升…

【Sublime详解】mac最好用的编辑器-配置-插件- Alignment-费元星

######################## 先同步下自己用了8年多的配置&#xff1a; mac 打开配置快捷键&#xff1a;command, 逗号 // Settings in here override those in "Default/Preferences.sublime-settings", // and are overridden in turn by syntax-specific setti…

代码随想录算法训练营第三十一天| 455.分发饼干、376.摆动序列、53.最大子序和

系列文章目录 目录 系列文章目录455.分发饼干贪心算法大饼干喂胃口大的&#xff08;先遍历胃口&#xff09;胃口大的先吃大饼干(先遍历饼干&#xff09;小饼干先喂胃口小的&#xff08;先遍历胃口&#xff09;胃口小的先吃小饼干&#xff08;先遍历饼干&#xff09; 376. 摆动序…

ABR 车路协同场景

#include <iostream> #include <sstream> #include <vector> #include <regex> #include <string> using namespace std; int main() { // 创建一个字符串&#xff0c;用于存储输入 string input; // 从标准输入读取一行 get…

原 Excel 文件中的偶数行替换成对应上下两行的平均值

实现代码 import openpyxl# 打开Excel文件 input_file input.xlsx output_file input3.xlsx wb openpyxl.load_workbook(input_file) output_wb openpyxl.Workbook()# 处理每个工作表 for sheet_name in wb.sheetnames:sheet wb[sheet_name]# 新建一个工作表&#xff0c;…

14-pyspark的DataFrame使用总结

目录 前言DataFrame使用总结 DataFrame的构建方法1&#xff1a;通过列表构建方法2&#xff1a;通过Row对象构建方法3&#xff1a;通过表Schema构建 方法4&#xff1a;rdd结合字符串构建 DataFrame的方法 PySpark实战笔记系列第五篇 10-用PySpark建立第一个Spark RDD(PySpark实战…