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

priority_queue的使用和模拟实现以及仿函数

目录

  • 1. priority_queue
    • 1.1 priority_queue的介绍
    • 1.2 priority_queue的使用
    • 1.3 priority_queue的模拟实现
  • 2. 仿函数
    • 2.1 什么是仿函数
    • 2.2 增加仿函数的priority_queue模拟实现完整代码
    • 2.3自定义仿函数
    • 2.4 仿函数的其他用法

1. priority_queue

1.1 priority_queue的介绍

priority_queue文档介绍

  1. 优先队列是一种容器适配器(将特定容器类封装作为其底层容器类),根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  2. 优先队列类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
  3. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
    empty():检测容器是否为空
    size():返回容器中有效元素个数
    front():返回容器中第一个元素的引用
    push_back():在容器尾部插入元素
    pop_back():删除容器尾部元素
  4. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。
    在这里插入图片描述
    priority_queue默认是一个大堆,这是由第三个参数less(仿函数)所控制的,虽然less明面意思是小于,但是默认是一个大堆,默认大的优先级较高,大的先出。greater(仿函数),明面意思是大于,但是是一个小堆,小的优先级比较高,小的先出。

1.2 priority_queue的使用

函数声明接口说明
priority_queue()/priority_queue(first, last)构造一个优先级队列
empty()检测优先级队列是否为空,是返回true,否则返回false
top()返回优先级队列中最大(最小元素),即堆顶元素
push(val)在优先级队列中插入元素val
pop()删除优先级队列中最大(最小)元素,即堆顶元素
#include <iostream>
#include <queue>
#include <algorithm>using namespace std;int main()
{// 默认大的数优先级高priority_queue<int> pq;// 第三个模板参数传greater类型小的数优先级高//priority_queue<int, vector<int>, greater<int>> pq;// 第三个模板参数传less类型大的数优先级高//priority_queue<int, vector<int>, less<int>> pq;pq.push(1);pq.push(3);pq.push(5);pq.push(2);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;vector<int> v = { 1, 3, 6, 7, 9, 2, 5 };// < 升序//sort(v.begin(), v.end());// > 降序sort(v.begin(), v.end(), greater<int>());//sort(v.rbegin(), v.rend());// 为什么priority_queue<int, vector<int>, greater<int>> pq;和// sort(v.begin(), v.end(), greater<int>());中的传的greater不一样?// 因为一个是模板参数要传类型,另一个是函数参数要传对象for (auto e : v){cout << e << " ";}cout << endl;return 0;
}

1.3 priority_queue的模拟实现

关于向上调整法、向下调整法和向下调整建堆可以参考二叉树详解中的向上(向下)调整算法和堆排序

这里先不实现有仿函数的版本,等下面了解仿函数再实现

#pragma once
#include <vector>namespace bs
{template<class T, class Container = vector<T>>class priority_queue{public:priority_queue() = default;template <class InputIterator>priority_queue(InputIterator first, InputIterator last):_con(first, last){// 向下调整建堆for (int i = (_con.size() - 1 - 1) / 2; i >= 0; --i){adjust_down(i);}}void push(const T& val){_con.push_back(val);adjust_up(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}const T& top() const{return _con[0];}bool empty() const{return _con.empty();}size_t size() const{return _con.size();}private:void adjust_up(int child){int parent = (child - 1) / 2;while (child > 0){if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else break;}}void adjust_down(int parent){int child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && _con[child] < _con[child + 1])++child;if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else break;}}private:Container _con;};
}
#include "priority_queue.h"int main()
{//bs::priority_queue<int> pq;//priority_queue选择vector作为默认底层容器,是因为vector的连续内存//、随机访问和高效尾部操作特性,完美适配了堆(priority_queue的核心//实现)的存储和操作需求,能够在插入、删除和访问顶部元素时提供最优效率。vector<int> v = { 1, 3, 6, 7, 9, 2, 5 };bs::priority_queue<int> pq(v.begin(), v.end());pq.push(1);pq.push(3);pq.push(5);pq.push(2);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;return 0;
}

2. 仿函数

2.1 什么是仿函数

  1. 仿函数是模板函数,其实就是一个只有operator()运算符重载的空类,空类的大小是一个字节,这个类的对象可以像函数一样使用。
  2. 仿函数的作用不止有less和great的比较大小,还有其他作用,下面会介绍。

记得C语言给我们提供了一个qsort函数,这个函数用到了函数指针。这个函数指针指向一个比大小的函数,但是函数指针太麻烦了而且有缺陷,所以C++就引入了仿函数。

下面通过仿函数改写的冒泡排序来理解仿函数。

// 仿函数
template<class T>
struct Less
{// 只实现一个operaotr()bool operator()(const T& x, const T& y){return x < y;}
};template<class T>
struct Greater
{bool operator()(const T& x, const T& y){return x > y;}
};// < 升序
// > 降序
// 用模板来控制比较的类型(class T)和比较的方式(class Compare)
template<class T, class Compare>
void BubbleSort(T* a, int n, Compare com)
{int exchange = 0;for (int i = 0; i < n; i++){for (int j = 0; j < n - i - 1; j++){//if (a[j + 1] > a[j])if (com(a[j + 1], a[j])){exchange = 1;swap(a[j], a[j + 1]);}}if (exchange == 0){break;}}
}int main()
{//仿函数的速度不一定比普通函数慢Less<int> lessFunc; // 类实例化对象cout << lessFunc(1, 2) << endl; // 仿函数像函数一样使用cout << lessFunc.operator()(1, 2) << endl; // 实际是调用operator()int a[] = { 1, 2, 4, 6, 5, 9, 3, 7 };//BubbleSort(a, 8, Less<int>());// 仿函数可以像函数指针一样使用,弥补了函数指针不能作为类型传给函数模板的缺点BubbleSort(a, 8, lessFunc);//BubbleSort(a, 8, Greater<int>());for (auto e : a){cout << e << " ";}cout << endl;return 0;
}

2.2 增加仿函数的priority_queue模拟实现完整代码

#pragma once
#include <vector>template<class T>
struct Less
{bool operator()(const T& x, const T& y){return x < y;}
};template<class T>
struct Greater
{bool operator()(const T& x, const T& y){return x > y;}
};namespace bs
{template<class T, class Container = vector<T>, class Compare = Less<T>>class priority_queue{public:priority_queue() = default;template <class InputIterator>priority_queue(InputIterator first, InputIterator last):_con(first, last){// 向下调整建堆for (int i = (_con.size() - 1 - 1) / 2; i >= 0; --i){adjust_down(i);}}void push(const T& val){_con.push_back(val);adjust_up(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}const T& top() const{return _con[0];}bool empty() const{return _con.empty();}size_t size() const{return _con.size();}private:void adjust_up(int child){int parent = (child - 1) / 2;while (child > 0){if (_com(_con[parent], _con[child])){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else break;}}void adjust_down(int parent){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]);parent = child;child = parent * 2 + 1;}else break;}}private:Container _con;Compare _com;};
}
#include "priority_queue"
int main()
{//bs::priority_queue<int> pq;bs::priority_queue<int, vector<int>, Greater<int>> pq;pq.push(1);pq.push(3);pq.push(5);pq.push(2);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;return 0;
}

2.3自定义仿函数

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);
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}struct PDateLess
{bool operator()(const Date* p1, const Date* p2){return *p1 < *p2;}
};int main()
{// 默认是按new出来的地址比较的,每次比较的结果可能不同bs::priority_queue<Date*, vector<Date*>> q1;// 传PDateLess按照Date类中的方法比较//bs::priority_queue<Date*, vector<Date*>, PDateLess> q1;q1.push(new Date(2018, 10, 29));q1.push(new Date(2018, 10, 28));q1.push(new Date(2018, 10, 30));while (!q1.empty()){cout << *q1.top() << " ";q1.pop();}cout << endl;return 0;
}

2.4 仿函数的其他用法

仿函数不止能用来比较大小,也可以是满足某种条件就执行操作

struct OP1
{bool operator()(int x){return x % 2 == 0;}
};struct OP2
{int operator()(int x){if (x % 2 == 0) return x * 2;else return x;}
};int main()
{int a[] = { 1, 2, 4, 6, 5, 9, 3, 7 };// 查找第一个偶数// find_if 查找第一个满足某条件的数auto it = find_if(a, a + 8, OP1());cout << *it << endl;vector<int> v = { 1, 2, 4, 6, 5, 9, 3, 7 };// 让偶数都 * 2// transform 让v.begin() ~ v.end() 中的元素按照OP2的规则放入以v.begin()开头的区间中transform(v.begin(), v.end(), v.begin(), OP2());for (auto e : v){cout << e << " ";}cout << endl;return 0;
}
http://www.xdnf.cn/news/15310.html

相关文章:

  • FatJar打包和FatJar启动配置文件修改。
  • 对偶原理与蕴含定理
  • [论文阅读] 人工智能 + 软件工程 | 用大语言模型+排名机制,让代码评论自动更新更靠谱
  • Ubuntu22.04 python环境管理
  • 深度解析:htmlspecialchars 与 nl2br 结合使用的前后端协作之道,大学毕业论文——仙盟创梦IDE
  • nginx:SSL_CTX_use_PrivateKey failed
  • 【HTTP版本演变】
  • Python 数据建模与分析项目实战预备 Day5 - 模型训练与评估
  • 九、官方人格提示词汇总(中-1)
  • (LeetCode 每日一题) 1290. 二进制链表转整数 (链表+二进制)
  • Kafka 时间轮深度解析:如何O(1)处理定时任务
  • 前端docx库实现将html页面导出word
  • 【第一章编辑器开发基础第二节编辑器布局_3间距控制(4/4)】
  • Java 大视界 -- 基于 Java 的大数据可视化在城市地下管网管理与风险预警中的应用
  • 显示器核心三要素详解:刷新率、分辨率、色深
  • SpringBoot-26-企业云端开发实践之Vue框架状态管理VueX和数据模拟MockJS
  • 从零构建搜索引擎 build demo search engine from scratch
  • MIPI DSI(三) MIPI DSI 物理层和 D-PHY
  • MMpretrain 中的 LinearClsHead 结构与优化
  • C++标准库(std)详解
  • 1.连接MySQL数据库-demo
  • 蜻蜓I即时通讯水银版系统直播功能模块二次开发文档-详细的直播功能模块文档范例-卓伊凡|麻子
  • 第十八篇 数据清洗:Python智能筛选与统计:从海量Excel数据中秒级挖掘,辅助决策!你的数据分析利器!
  • hash表的模拟--开放定址法
  • C++模版编程:类模版与继承
  • 力扣 hot100 Day43
  • 2025.7.13总结
  • 代码部落 20250713 CSP-S复赛 模拟赛
  • 芯片相关必备
  • [附源码+数据库+毕业论文+答辩PPT+部署教程+配套软件]基于SpringBoot+MyBatis+MySQL+Maven+Vue实现的交流互动管理系统