当前位置: 首页 > news >正文

c++STL-优先队列priority_queue和仿函数

建议先看c++STL-STL简介和vector的使用。

优先队列priority_queue

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

优先队列是优先级队列,即传统的队列中优先级高的先被取出。

优先级队列也是容器适配器,底层容器是vector

尽管底层是数组,但底层的实现是堆也就是一种特殊的完全二叉树(区分结点等级的完全二叉树)。

优先队列也是容器适配器,默认生成的构造函数会去调用容器的构造函数,析构函数同理,因此不需要刻意去实现。

三个模板参数:

T数据类型

Container底层容器,默认是STL的容器vector

Compare仿函数,负责完成比较功能,默认是less。即若存的数据只是数字,则优先队列的底层是大根堆。

c++98中对less的定义:

template <class T>
struct less : binary_function <T,T,bool> {bool operator() (const T& x, const T& y) const {return x<y;}
};

优先队列的底层就是vector改装成的完全二叉树。将这里也就是堆的内容移植过来也能使用。

仿函数

仿函数是模板类,它需要重载(),即operator(),用这个()作为比较器。因为是模板类,所以实际运行效率比普通函数要低(进行同样的业务比普通函数慢)。

优先级队列中的默认仿函数是less,在less下堆默认是大堆。

less的底层:

template <class T>
struct less : binary_function <T,T,bool> {bool operator() (const T& x, const T& y) const {return x<y;}
};

less : binary_function 表示less继承自类binary_function,继承方式是公有(public)继承。

仿函数的直接使用

仿函数有时称作函数对象,是因为它能像函数一样调用。

#include<iostream>
//直接调用仿函数需要展开头文件functional
#include<functional>
using namespace std;int main() {less<int> le;cout << le(2, 3) << endl;//2>3,输出1cout << le.operator()(3,3)<< endl;//3不大于3,输出0return 0;
}

仿函数的出现是为了替代函数指针

因为函数指针的声明复杂,且函数指针不能在模板参数上传(指针变量不能作为类型上传模板参数),于是c++用类的对象来代替,即在容器或容器适配器中配置仿函数类的对象,通过对象间接调用和比较有关的操作符来完成各种操作。

例如某个STL的空间配置器stl_alloc.h中的函数指针:

static void (* set_malloc_handler(void (*f)()))() {void (* old)() = __malloc_alloc_oom_handler;__malloc_alloc_oom_handler = f;return(old);
}

若c语言功底不够都无法看出它是什么,将它拆解成这样就能表达它的意思:

static void(*)() set_malloc_handler(void(*)() f){}
//将void(*)()类比成int就能理解这个函数
static int set_malloc_handler(int f){}

仿函数作为形参或模板参数

algorithm库里很多工具比如sort是函数模板,需要上传仿函数(匿名)对象,而priority_queue是类模板,<>内的模板参数上传的是类型。

#include<iostream>
#include<queue>
using namespace std;int main() {int a[] = { 1,1,4,5,1,4,0,7,2,1 };priority_queue<int, vector<int>, less<int> >pq;sort(a, a + 10, less<int>());//上传仿函数的匿名对象//还是范围for,但类型固定为intfor (int x : a) { //上传less时是降序pq.push(x);cout << x << ' ';}cout << endl;//依次取出优先队列(完全二叉树)的堆顶while (!pq.empty()) {cout << pq.top() << ' ';pq.pop();}return 0;
}

仿函数可以自己写,使得容器能更好地处理用户给的自定义类型。

#include<iostream>
#include<queue>
using namespace std;struct mycmp {bool operator()(int& a, int& b) {return a > b;}
};int main() {int a[] = { 1,1,4,5,1,4,0,7,2,1 };priority_queue<int, vector<int>, mycmp>pq;sort(a, a + 10, mycmp());//上传自定义仿函数的匿名对象for (int x : a) { //升序pq.push(x);cout << x << ' ';}cout << endl;//依次取出优先队列(完全二叉树)的堆顶while (!pq.empty()) {cout << pq.top() << ' ';pq.pop();}return 0;
}

模拟优先队列

参考c++98的接口:
请添加图片描述

以及c++STL-string的模拟实现、c++STL-vector的模拟实现即可。

因为模板不方便进行声明和定义分明,所以将它们放一起。

#pragma once
#include<vector>
#include<functional>
using std::vector;
using std::less;
using std::swap;//这里偷懒,使用库里的容器和仿函数
template<class T,class Container=vector<T>,class Compare=less<T> >
class mypriority_queue {
public://构造函数mypriority_queue() { }template<class InputIterator>mypriority_queue(InputIterator first, InputIterator last) {for (auto x = first; x != last; x++)push(*first);}//析构函数~mypriority_queue() { }//判空bool empty()const {return container.size() == 0;}//优先队列元素个数size_t size()const {return container.size();}//返回堆顶T top()const {return container[0];}//插入元素void push(const T& x) {container.push_back(x);adjustUp(container.size() - 1);}//弹出堆顶void pop() {swap(container[0], container[container.size() - 1]);container.pop_back();adjustDown(0);}
private://堆的向下调整算法void adjustDown(int parent) {int child = parent * 2 + 1;while (child < (int)container.size()) {if (child + 1 < (int)container.size())if (compare(container[child + 1] , container[child]))++child;//优先队列中更像是前者和后者满足什么条件就进行交换if (compare(container[child] , container[parent])) {swap(container[child], container[parent]);parent = child;child = parent * 2 + 1;}elsebreak;}}//堆的向上调整算法void adjustUp(int child) {int parent = (child - 1) / 2;while (child > 0) {if (compare(container[child] , container[parent])) {swap(container[child], container[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}}Container container;Compare compare;
};

优先队列有关OJ

堆在贪心算法中有很广泛的应用,且本身和堆排序有很强的关联性。

373. 查找和最小的 K 对数字 - 力扣

373. 查找和最小的 K 对数字 - 力扣(LeetCode)

很容易想到,遍历2个数组,组成k个符合条件的数对即可(详细见堆的应用——topk问题-)。

2个数组nums1nums2的数据量是10510^5105,2层循环进行枚举的话会超时。

因为2个数组都是有序的,所以可以用一个监测变量cnt,若事先准备好的优先队列有更新则修改cnt,否则不做处理,当循环检测到cnt没有发生变化时直接跳出循环。

typedef pair<int,int>pii;class Greate{
public:bool operator()(pii a,pii b){return a.first+a.second<b.first+b.second;}
};class Solution {
public:vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {priority_queue<pii,vector<pii>,Greate>q;for(auto&x:nums1){int cnt=0;for(auto&y:nums2)if(q.size()<k){q.push({x,y});++cnt;}else if(x+y<q.top().first+q.top().second){++cnt;q.pop();q.push({x,y});}if(cnt==0)break;}vector<vector<int> >ans;while(q.size()){ans.push_back({q.top().first,q.top().second});q.pop();}return ans;}
};
http://www.lryc.cn/news/585139.html

相关文章:

  • CS144 lab2 tcp_receiver
  • 机器学习之线性回归(七)
  • TransUnet医学图像分割模型
  • 如何设置直播间的观看门槛,让直播间安全有效地运行?
  • 解锁48V USB-C供电潜力,慧能泰重磅推出PD3.2 DRP芯片HUSB253
  • Flutter优缺点
  • Koa+Puppeteer爬虫教程页面设计
  • 【java17】使用 Word 模板导出带替换符、动态表格和二维码的文档
  • 格式规范公文处理助手:一键排版 标题 / 正文 / 页码一键调,Word 脚本自定义
  • 专题:2025云计算与AI技术研究趋势报告|附200+份报告PDF、原数据表汇总下载
  • 关闭 GitLab 升级提示的详细方法
  • 基于gitlab 构建CICD发布到K8S 平台
  • Tomcat问题:启动脚本startup.bat中文乱码问题解决
  • 信号肽预测工具PrediSi本地化
  • 【flutter】flutter网易云信令 + im + 声网rtm从0实现通话视频文字聊天的踩坑
  • CentOS 安装 JDK+ NGINX+ Tomcat + Redis + MySQL搭建项目环境
  • 『 C++ 入门到放弃 』- 多态
  • MyBatis-Plus通用中等、大量数据分批查询和处理
  • c语言中的数组IV
  • 卸载软件总留一堆“垃圾”?这款免费神器,一键扫清注册表和文件残留!
  • Python shutil模块详解
  • GPT3/chatGPT/T5/PaLM/LLaMA/GLM主流大语言模型的原理和差异
  • 从零实现一个GPT 【React + Express】--- 【3】解析markdown,处理模型记忆
  • 【LeetCode 热题 100】146. LRU 缓存——哈希表+双向链表
  • 0102基础补充_交易演示-区块链-web3
  • Django母婴商城项目实践(二)
  • 机器学习数据集划分全指南:train_test_split详解与实践
  • 基于相似性引导的多视角功能性脑网络融合|文献速递-最新论文分享
  • 【科研绘图系列】R语言绘制系统发育树和柱状图
  • 思维链革命:让大模型突破“机器思考”的边界