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

priority_queue(优先级队列)和仿函数

文章目录

  • 前言
  • 优先级队列
    • 优先级队列的实现
      • 优先级队列基本框架
      • 向上调整和向下调整
    • 建堆
  • 仿函数/函数对象
    • 仿函数的运用
  • 将仿函数融入优先级队列中

前言

本节小编会结合优先级队列去探讨仿函数的由来以及作用。

优先级队列

定义:优先级队列是一种特殊的队列,它会按照元素的优先级顺序出队,而不是按照先进先出(FIFO)的顺序。优先级队列通常基于堆(Heap)数据结构实现,分为最大堆和最小堆。

注意:优先级队列并不是一个先进先出的队列,它是由容器适配器适配出的一个堆结构。

优先级队列的实现

优先级队列基本框架

  • 先看下方的代码
  • 用命名空间封装防止和标准库中的优先级队列冲突
  • 定义三个模板参数
    • T:容器内对象类型
    • Container:容器适配器
    • Compare:仿函数(放后面讲解)
  • priority_queue()=default:让编译器生成默认构造(由于构造不是本章重点)

为什么我们的默认容器适配器要选用vector呢?

由于优先级队列通常基于堆数据结构实现,而堆需要一个随机访问容器std::vector 提供了随机访问特性,能够高效地支持堆操作,例如:

  • 访问堆顶元素:可以直接通过索引访问第一个元素。
  • 调整堆结构:在插入或删除元素时,需要对堆进行调整(如上浮或下沉操作),这需要随机访问元素。

std::vector 的连续内存布局使得这些操作非常高效,而其他容器(如 std::list 或 std::deque)无法提供这种高效的随机访问能力。

nampspace dhb
{template<class T,class Container=vector<T>,class Compare=Less<int>>class priority_queue{//......priority_queue()=defaultprivate:Container _con;}}

向上调整和向下调整

在堆里最基本的插入删除操作都是需要借助这些调整算法
故带大家快速回顾一下调整算法:

  • 向上调整算法
//以大堆为例
void adjust_up(size_t child)
{size_t parent=(child-1)/2;while(child>0){if(_con[parent]<_con[child]){std::swap(_con[parent],_con[child]);child=parent;parent=(child-1)/2;}else{break;}}
}

进一步实现插入操作

void push(const T& x)
{//先插入再调整_con.push_back();adjust_up(_con.size()-1);
}

向下调整算法

void adjust_down(size_t parent)
{size_t child=parent*2+1;while(child<_con.size()){if(_con[child+1]<_con.size()&&_con[child]<_con[child+1])child++;//找到最大孩子if(_con[parent]<_con[child]){std::swap(_con.[child],_con.[parent]);parent=child;child=parent*2+1;}else{break;}}	
}

进一步实现插入操作

void pop()
{std::swap(_con[0],_con[_con,size()-1]);_con.pop_back();adjust_down(0);
}

为什么每次的插入删除操作后都要进行相关调整操作呢?

  • 对于插入操作来说每插入一个数都要看这个数是否满足堆结构,所以要依次和自己的父亲比较(其他的兄弟结点都已经满足树结构不用调整),即向上调整。
  • 而删除操作为什么不直接进行头删然后挪动数据呢?
    如果这样操作会有兄弟变父亲的现象,整个二叉树的结构会彻底破坏。
    所以我们采用先进行头尾交换再尾删,这样整体的二叉树结构没有被破坏,调整也只是一支走下去不用全部调整。

建堆

为什么一般采用向下调整算法建堆?

向上调整建堆时间复杂度较高O(n*logn)
因为越向下结点要走的深度越高有一半的结点要走整个树的深度
(结点最多的一层调整次数也最多)
而向下调整(结点最多的一层不需要调整)O(n)

这里的向下调整建堆会被运用于迭代器区间的构造中

template<class InpushIterator>
priority_queue(InpushIterator first,InpushIterator end):_con(first,last)
{//向下调整建堆for(size_t i=0,i<(_con.size-1-1)/2;i++){adjust_down(i);}	
}

仿函数/函数对象

仿函数的定义:仿函数是一种特殊的函数对象,它是一个类的对象,通过重载 operator(),使其能够像函数一样被调用。仿函数在 C++ 中广泛应用于标准库和自定义算法中,用于封装可调用的逻辑。

其实仿函数出现的意义就是来取代C语言中的函数指针,函数指针过于复杂难用,而且函数指针不能用于类模板中,类模板是传一个类型过来其类型可以不确定,但是函数指针的类型是确定的。
运用函数指针在C++11中提出了解决方案但在这里不做讲解,这里我们会提供另外一种解决方案:仿函数。

例如,以下两个函数的指针类型是不同的:

void f1(int);
int f2(double);

对应的函数指针类型分别为:

void (*pf1)(int);
int (*pf2)(double);

类模板在定义时是泛型的,其类型参数在编译时才被具体化。例如:

template <typename T>
class MyClass {T value;
};

在实例化 MyClass<int>MyClass<double> 时,T 才被具体化为 intdouble

仿函数的运用

我先根据定义写一个仿函数大伙就明白它为啥叫仿函数了。

template<class T>
class Less
{
public:bool operator()(const T& x, const T& y){return x < y;}
};
template<class T>
class Greater
{
public:bool operator()(const T& x, const T& y){return x > y;}
};
int main()
{Less<int>  less;cout << less(3,2) << endl;return 0;
}

我们发现cout << less(3,2) << endl;这里对象less的调用就像函数一样,所以我们又称仿函数为函数对象。

运用仿函数调节比较大小逻辑
这里我们以冒泡排序为例讲解:

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;}}
}

这样if (com(a[j + 1], a[j]))修改冒泡排序后我们就可以通过传第三个模板参数来调节比大小的逻辑:

int a[] = { 1,2,3,2,5,7,1 };
BubbleSort(a, 7, Less<int>());
for (auto& e : a)
{cout << e << " ";
}
cout << endl;BubbleSort(a, 7, Greater<int>());
for (auto& e : a)
{cout << e << " ";
}
cout << endl;

在这里插入图片描述

将仿函数融入优先级队列中

好处?

  1. 封装性:仿函数是一种封装了可调用逻辑的对象。通过使用仿函数,可以将比较逻辑封装在一个类中,而不是直接传递一个函数指针。这种方式不仅提高了代码的可读性和可维护性,还可以携带状态信息。
  2. 灵活性:优先级队列的核心功能是根据元素的优先级顺序管理元素。默认情况下,优先级队列使用最大堆(即优先级最高的元素是最大的元素),但用户可能需要根据自己的需求定义不同的优先级规则。通过使用仿函数,用户可以自定义比较逻辑(一个类中我既可以比较它的对象大小,也可以比较它们的指针大小),从而使优先级队列能够适应各种不同的应用场景。
  • 注意:在库中传仿函数Less是生成大堆,greater是生成小堆(与字面意思相反)

具体实现:

#pragma once
#include<vector>
//库中的仿函数比较逻辑是反的
//传less大的优先级高(大堆),传greater小的优先级高 
template<class T>
class Less
{
public:bool operator()(const T& x, const T& y){return x < y;}
};
template<class T>
class Greater
{
public:bool operator()(const T& x, const T& y){return x > y;}
};
namespace dhb
{	template<class T, class Container = std::vector<T>,class Compare=Less<T>>class priority_queue{public://强制生成默认构造priority_queue() = default;template<class InpushIterator>//迭代器区间构造priority_queue(InpushIterator first, InpushIterator last):_con(first, last){for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--){adjust_down(i);  }}void push(const T& x){//可以用算法库里的push_heap_con.push_back(x);	adjust_up(_con.size() - 1);}void pop(){std::swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}const T& top(){return _con[0];}bool empty()const{return _con.empty();}private:void adjust_up(size_t child){Compare com;size_t parent = (child - 1) / 2;while (child > 0){if (com(_con[parent],_con[child])){std::swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void adjust_down(size_t parent){Compare com;size_t 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])){std::swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}elsebreak;}}	private:Container _con;};
}
http://www.lryc.cn/news/617956.html

相关文章:

  • 关于linux系统编程2——IO编程
  • 内网依赖管理新思路:Nexus与CPolar的协同实践
  • redis常见的性能问题
  • Redis 数据倾斜
  • day072-代码检查工具-Sonar与maven私服-Nexus
  • Qt 5.14.2安装教程
  • 基于Qt Property Browser的通用属性系统:Any类与向量/颜色属性的完美结合
  • 学习嵌入式第二十五天
  • QT QVersionNumber 比较版本号大小
  • office卸载不干净?Office356卸载不干净,office强力卸载软件下载
  • MySQL 索引(重点)
  • AT24C02C-SSHM-T用法
  • leecode875 爱吃香蕉的珂珂
  • 每日一题:2的幂数组中查询范围内的乘积;快速幂算法
  • 工业数采引擎-通信协议(Modbus/DTU/自定义协议)
  • 【Linux】重生之从零开始学习运维之防火墙
  • C++ 限制类对象数量的技巧与实践
  • AcWing 6479. 点格棋
  • ​费马小定理​
  • 前端组件库双雄对决:Bootstrap vs Element UI 完全指南
  • Unknown collation: ‘utf8mb4_0900_ai_ci‘
  • 软考 系统架构设计师系列知识点之杂项集萃(121)
  • mysql基础(二)五分钟掌握全量与增量备份
  • OCSSA-VMD-Transformer轴承故障诊断,特征提取+编码器!
  • 视频剪辑的工作流程
  • socket编程TCP
  • 自然语言处理实战:用LSTM打造武侠小说生成器
  • 银河通用招人形机器人强化学习算法工程师了
  • IoT/透过oc_lwm2m/boudica150 源码中的AT指令序列,分析NB-IoT接入华为云物联网平台IoTDA的工作机制
  • openpnp - 顶部相机环形灯光DIY