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

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.lryc.cn/news/587582.html

相关文章:

  • 《C++内存泄漏8大战场:Qt/MFC实战详解 + 面试高频陷阱破解》
  • MFC/C++语言怎么比较CString类型最后一个字符
  • 【Linux】Ubuntu22.04安装zabbix
  • HTTP 四种常见方法
  • 【论文阅读】Thinkless: LLM Learns When to Think
  • ubuntu(22.04)系统上安装 MuJoCo
  • MFC UI控件CheckBox从专家到小白
  • 桶排序算法深度剖析
  • FastAPI + gRPC 全栈实践:Windows 开发到 Ubuntu 部署全指南
  • flink 和 spark 架构的对比
  • idea删除的文件怎么找回
  • IDEA中使用Servlet,tomcat输出中文乱码
  • JMeter 连接与配置 ClickHouse 数据库
  • 递推预处理floor(log_2{n})
  • 【脚本系列】如何使用 Python 脚本对同一文件夹中表头相同的 Excel 文件进行合并
  • uniapp video视频全屏播放后退出,页面字体变大,样式混乱问题
  • 基于Spring Boot的生活用品电商网站的设计与实现
  • 国内隧道IP代理技术解析:原理、优势与实战应用
  • 算法学习笔记:21.动态规划——从原理到实战,涵盖 LeetCode 与考研 408 例题
  • linux 文件搜索与文件内容查看
  • Imx6ull用网线与电脑连接
  • 游戏玩法的专利博弈
  • 11、鸿蒙Harmony Next开发:列表布局 (List)
  • Spark 和 Hadoop MapReduce 的基本概念及区别
  • Spring Boot项目结构解析:构建高效、清晰的代码框架
  • UE5多人MOBA+GAS 22、创建技能图标UI,实现显示蓝耗,冷却,以及数字显示的倒数计时还有雷达显示的倒数计时
  • 【解决办法】越疆Dobot CR5 桌面客户端DobotStudio Pro连不上机器人
  • iOS高级开发工程师面试——Objective-C 语言特性
  • WPF的三轴机械手控件动画
  • MEMS IMU如何赋能无人机与机器人精准感知?