【C++】优先级队列以及仿函数

本篇我们来介绍一下优先级队列 priority_queue 。优先级队列的底层是数据结构中的,在C++中它是一个容器适配器,这个容器适配器比之前的栈和队列更复杂。

1.priority_queue的介绍

1.1 优先级队列的底层

因为优先级队列就是堆,堆的底层是数组,所以优先级队列的默认适配器也是数组

 需要用到访问下标的,数组一定是最好的选择。不了解堆的,建议先看下面这篇博客。

 数据结构中的堆:【数据结构】堆的概念、结构、模拟实现以及应用

1.2 文档介绍

文档介绍:priority_queue - C++ Reference

在C++中优先级队列的相关接口就是如上这些。这里的top,如果大的值优先级高,也就是大堆,top返回的就是堆里面的最大值,如果是小的数优先级高,也就是小堆,返回的就是最小值。

2.priority_queue的使用

使用优先级队列的时候需要包含头文件 #include <queue>

	priority_queue<int> pq;pq.push(5);pq.push(2);pq.push(1);pq.push(10);pq.push(8);pq.push(4);

数据结构中的实现逻辑如下。 用到的是向上调整算法。

 再通过top和pop的配合,把这些数打印出来。

while (!pq.empty())
{cout << pq.top() << " ";pq.pop();
}
cout << endl;

 优先级队列默认大的值优先级高,在数据结构中的意思就是,默认为大堆。

如果想换成小的优先级高,就是小堆,我们就要传greeter

priority_queue<int, vector<int>, greater<int>> pq;

改成greater,用同样的用例试试。

结果就是升序排列。 

 在数据结构中小堆就是要用到向下移调整,这里就不具体演示了,详情请看数据结构那篇博客。

3.priority_queue的模拟实现 

3.1 做准备工作

建立一个头文件priority_queue.h,一个源文件test.cpp,存放内容如下。

在 priority_queue.h 包含需要的头文件,所有实现用命名空间隔开。

#pragma once
#include <iostream>
#include <vector>
using namespace std;
namespace lyj
{template<class T, class Container = vector<T>>class priority_queue{public:private:Container _con;};
}

3.2 父节点和子节点的对应关系

假设父节点在数组的下标为i:

左孩子在数组的下标:2*i + 1;右孩子在数组的下标:2*i + 2;

假设子节点在数组中的下标为j:

父节点在数组中的下标:(j - 1) / 2;

3.2 插入数据

以大的数优先级高为例,在插入数据之前,这个堆一定已经是一个大堆了。这里的实现逻辑与数据结构中介绍的逻辑一样。

在 priority_queue.h 中实现。

3.2.1 Swap交换

因为需要交换数据,所以我们写一个交换函数。

void Swap(T& x1, T& x2)
{T tmp = x1;x1 = x2;x2 = tmp;
}

3.2.2 AdjustUp向上调整

把向上调整的代码也重新封装。

void AdjustUp(int child)
{int parent = (child - 1) / 2;while (child > 0 && _con[child] > _con[parent]){Swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}
}

因为有默认的this指针,所以对比数据结构中C语言实现的堆的向上调整,现在我们只要传一个参数过去。

3.3.3 push插入

插入数据直接用_con的插入,插入完之后,要进行调整,保证这始终是个堆。

void push(const T& x)
{_con.push_back(x); //插入数据AdjustUp(_con.size() - 1);//向上调整
}

test.cpp中测试一下。

void test1()
{lyj::priority_queue<int> pq;pq.push(1);pq.push(2);pq.push(3);pq.push(4);pq.push(5);
}int main()
{test1();return 0;
}

3.2 删除数据

在数据结构中介绍过,删除数据是删除堆顶数据。删除数据的逻辑是将收尾数据交换,交换后要删的数据在最后,然后再尾删,后用向下调整算法,将堆恢复。

在 priority_queue.h 中实现。

3.2.1 向下调整

 向下调整的逻辑和数据结构中一致。

void AdjustDown(size_t parent)
{size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && _con[child] < _con[child + 1]){child++;}if (_con[child] > _con[parent]){Swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

参数我们只需要传一个parent就可以了。

3.2.2 pop删除

void pop()
{Swap(_con[0], _con[_con.size() - 1]);//首尾交换_con.pop_back();//删除AdjustDown(0); //向下调整
}

test.cpp中测试一下。

void test1()
{lyj::priority_queue<int> pq;pq.push(1);pq.push(2);pq.push(3);pq.push(4);pq.push(5);pq.pop();pq.pop();
}

3.2.3 top、empty和size

在 priority_queue.h 中实现。

const T& top() 
{return _con[0];
}
bool empty() const
{return _con.empty();
}
size_t size() const
{return _con.size();
}

test.cpp中测试一下。

void test2()
{lyj::priority_queue<int> pq;pq.push(5);pq.push(2);pq.push(1);pq.push(10);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;
}

 

4.仿函数

我们前面说过,优先级队列默认为大堆,但是给第三个参数传greater过去,就可以改成小堆,这里说的less和greater就是仿函数。

目前我们只实现了一个大堆,要实现小堆就要把对比的逻辑全部改掉,但是库里面只需要传参数就可以了,如果我们想模拟实现的更接近库里面的优先级队列,就要先说说这个仿函数。

4.1 仿函数的介绍

仿函数本质是一个类,这个类重载了operator(),这个括号重载的是函数调用时参数列表的括号,它的对象可以像函数一样使用。

比如说我们现在要重载operator()实现两个数的比较

class Less
{
public:bool operator()(int x, int y){return x < y;}
};

我们还可以改造一下,用类模板。

template<class T>
class Less
{
public:bool operator()(const T& x, const T& y){return x < y;}
};

这个仿函数的类没有成员变量,所以大小是1字节。

我们来使用一下。

Less<int> lessF; //实例化出对象
cout << lessF(3, 2) << endl; //对象的使用

我们只看 cout << lessF(3, 2) << endl; 这句代码,会觉得我们是在使用一个函数,其实不是,这是一个类的对象,这个类的对象可以像函数一样使用。所以我们也把这个对象叫做函数对象

完整的写法是下面这样的。运算符重载。

cout << lessF.operator()(1, 2) << endl;

我们还可以写一个Greater类,里面的operator()也是实现两个数的比较,比较逻辑和Less相反。

template<class T>
class Greater
{
public:bool operator()(const T& x, const T& y){return x > y;}
};

4.2 仿函数的简单应用

有哪些使用场景呢?比如说我们这里有个冒泡排序,现在默认排升序

void BubbleSort(int* a, int n)
{for (int i = 0; i < n; i++){int flag = 0;for (int j = 0; j < n - 1 - i; j++){if (a[j + 1] < a[j]){int tmp = a[j + 1];a[j + 1] = a[j];a[j] = tmp;flag = 1;}}if (flag == 0){return;}}
}

我们想变成排降序,就要将代码 if (a[j + 1] < a[j]) 改成 if (a[j + 1] > a[j]) 

现在我们添加一个模板参数Compare cmp。

template<class Compare>
void BubbleSort(int* a, int n, Compare cmp)
{//...
}

 用cmp来控制比较大小的逻辑。

for (int j = 0; j < n - 1 - i; j++)
{
//	if (a[j] < a[j + 1]) 小于,排降序if (cmp(a[j], a[j + 1])){int tmp = a[j + 1];a[j + 1] = a[j];a[j] = tmp;flag = 1;}
}

我们在调用这个BubbleSort的时候,通过传的第三个参数,就是我们的仿函数,来控制比较逻辑

int main()
{Less<int> lessF; //实例化Greater<int> greaterF;int a[] = { 3, 6, 2, 7, 8, 1, 9, 4 };BubbleSort(a, 8, lessF); //降序BubbleSort(a, 8, greaterF); //升序return 0;
}

这里其实是走了一个回调的过程。

在传参数的时候还可以用匿名对象。

int a[] = { 3, 6, 2, 7, 8, 1, 9, 4 };
BubbleSort(a, 8, Less<int>()); //传匿名对象
BubbleSort(a, 8, Greater<int>()); //传匿名对象

 这个用在优先级队列里是怎样的呢?

4.3 priority_queue用仿函数实现

首先我们把自己实现的两个仿函数Greater和Less放到priority_queue.h里。可以选择放在namespace里面也可以不放。

然后我们也要在priority_queue类模板多加一个模板参数Compare,并且给缺省值,默认大的数优先级高,就是大堆。

template<class T, class Container = vector<T>, class Compare = Less<T>>

传参数的时候注意一下函数传参和类模板传参不一样。 

函数参数要传匿名对象,类模板的参数要传类型

因为我们传过来的是一个类型,所以要自己再定义一个对象。先改向上调整的代码。

void AdjustUp(int child)
{Compare cmp; //定义对象int parent = (child - 1) / 2;
//	while (child > 0 && _con[parent] < _con[child])while (child > 0 && cmp(_con[parent], _con[child])){Swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}
}

再用同样的逻辑改一下向下调整。

void AdjustDown(size_t parent)
{Compare cmp; //定义对象size_t child = parent * 2 + 1;while (child < _con.size()){//	if (child + 1 < _con.size() && _con[child] < _con[child + 1])if (child + 1 < _con.size() && cmp(_con[child], _con[child + 1])){child++;}//	if (_con[parent] < _con[child])if (cmp(_con[parent], _con[child])){Swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

只需要改动这两个函数,其余不变

有了仿函数,我们就不用写两个类一个大堆一个小堆了。

test.cpp中测试一下。

void test2()
{lyj::priority_queue<int> pq;pq.push(5);pq.push(2);pq.push(1);pq.push(10);pq.push(6);pq.push(3);pq.push(8);cout << pq.size() << endl;while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;
}

 

默认是大堆,然后结合top和pop按降序把数据打印出来。

再测试一下小堆,参数列表改一下就行。

 现在就是小堆,然后结合top和pop按升序把数据打印出来。

现在我们优先级队列的模拟实现就算完成了,我们下篇再见~ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/890168.shtml

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

相关文章

不需要服务器,使用netlify快速部署自己的网站

Netlify简介 1.1 Netlify的功能与特点 Netlify 是一个功能强大的静态网站托管平台&#xff0c;它不仅提供了简单的网站部署功能&#xff0c;还集成了许多现代化的开发工具和服务&#xff0c;帮助开发者更高效地构建、部署和管理网站。Netlify 的核心功能包括&#xff1a; 自动…

简单工厂模式和策略模式的异同

文章目录 简单工厂模式和策略模式的异同相同点&#xff1a;不同点&#xff1a;目的&#xff1a;结构&#xff1a; C 代码示例简单工厂模式示例&#xff08;以创建图形对象为例&#xff09;策略模式示例&#xff08;以计算价格折扣策略为例&#xff09;UML区别 简单工厂模式和策…

Unity动态读取外部图片转Texture2D,内存过大问题解决方案

问题描述 加载原始图片2.63M的图片,分辨率为3023*4032,占用内存108.5M 加载原始图片12.6 M的图片,分辨率为6000*8000,占用内存427.2M 太恐怖了吧 解决方案 1.加载完图片,等比缩放,宽高改为1024或者512以下 1024占用5.2M,512占用1.3M,相比小了很多 2.原始Texture2…

linux-----进程及基本操作

进程的基本概念 定义&#xff1a;在Linux系统中&#xff0c;进程是正在执行的一个程序实例&#xff0c;它是资源分配和调度的基本单位。每个进程都有自己独立的地址空间、数据段、代码段、栈以及一组系统资源&#xff08;如文件描述符、内存等&#xff09;。进程的组成部分&am…

ArkTs组件的学习

一. AlphabetIndexer 可以与容器组件联动用于按逻辑结构快速定位容器显示区域的组件 参数名类型必填说明arrayValueArray<string>是字母索引字符串数组&#xff0c;不可设置为空selectednumber是初始选中项索引值若超出索引值范围则取默认值0 class Lxr{tImg:Resource…

51c自动驾驶~合集42

我自己的原文哦~ https://blog.51cto.com/whaosoft/12888355 #DriveMM 六大数据集全部SOTA&#xff01;最新DriveMM&#xff1a;自动驾驶一体化多模态大模型&#xff08;美团&中山大学&#xff09; 近年来&#xff0c;视觉-语言数据和模型在自动驾驶领域引起了广泛关注…

Linux限制root 用户的远程登录(安全要求)

前言&#xff1a;现在基本用户主机都不允许使用root来操作&#xff0c;所以本文通过创建新用户&#xff0c;并限制root用户的ssh来解决这个问题 1. 创建新账户 aingo 首先&#xff0c;使用 root 账户登录系统。 sudo useradd aingo设置 aingo 账户密码&#xff1a; sudo pa…

前端学习笔记-Vue篇-04

4 Vue中的ajax 4.1 解决开发环境Ajax跨域问题 vue脚手架配置代理 配置参考 | Vue CLI方法一&#xff1a;在vue.config.js中添加如下配置: module.exports {devServer: {proxy: http://localhost:4000} } 说明: 1.优点:配置简单&#xff0c;请求资源时直接发给前端(8080)即…

【优选算法篇】位运算小课堂:从入门到精通的奇妙之旅(上篇)

文章目录 须知 &#x1f4ac; 欢迎讨论&#xff1a;如果你在学习过程中有任何问题或想法&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习。你的支持是我继续创作的动力&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;觉得这篇文章对你有帮助吗&#xff1…

【Linux】重启系统后开不开机(内核模块丢失问题)

问题 重启后开不开机报错如下&#xff1a; FAILED failed to start load kernel moduiles 可以看到提示module dm_mod not found 缺少了dm_mod 在内核module目录中 reboot重启可以看到这个现象&#xff1a; 可以看到重启启动磁盘&#xff0c;加载不到root 原因 dm_mod模块…

【现代服务端架构】传统服务器 对比 Serverless

在现代开发中&#xff0c;选择合适的架构是至关重要的。两种非常常见的架构模式分别是 传统服务器架构 和 Serverless。它们各有优缺点&#xff0c;适合不同的应用场景。今天&#xff0c;我就带大家一起对比这两种架构&#xff0c;看看它们的差异&#xff0c;并且帮助你选择最适…

搭建Tomcat(四)---Servlet容器

目录 引入 Servlet容器 一、优化MyTomcat ①先将MyTomcat的main函数搬过来&#xff1a; ②将getClass()函数搬过来 ③创建容器 ④连接ServletConfigMapping和MyTomcat 连接&#xff1a; ⑤完整的ServletConfigMapping和MyTomcat方法&#xff1a; a.ServletConfigMappin…

WPF DataTemplate 数据模板

DataTemplate 顾名思义&#xff0c;数据模板&#xff0c;在 wpf 中使用非常频繁。 它一般用在带有 DataTemplate 依赖属性的控件中&#xff0c;如 ContentControl、集合控件 ListBox、ItemsControl 、TabControls 等。 1. 非集合控件中使用 <UserControl.Resources>&l…

Python中exifread库使用

目录 简要介绍 库的安装 使用案例 常见问题 简要介绍 exifread 是一个用于读取图像文件 EXIF 元数据的 Python 库&#xff0c;能够提取图片的隐藏信息&#xff0c;包括经纬度、拍摄时间等信息。 库的安装 使用exifread库首先要确保已经安装 pip install exifread 使用…

clickhouse-数据库引擎

1、数据库引擎和表引擎 数据库引擎默认是Ordinary&#xff0c;在这种数据库下面的表可以是任意类型引擎。 生产环境中常用的表引擎是MergeTree系列&#xff0c;也是官方主推的引擎。 MergeTree是基础引擎&#xff0c;有主键索引、数据分区、数据副本、数据采样、删除和修改等功…

网络安全(3)_安全套接字层SSL

4. 安全套接字层 4.1 安全套接字层&#xff08;SSL&#xff09;和传输层安全&#xff08;TLS&#xff09; &#xff08;1&#xff09;SSL/TLS提供的安全服务 ①SSL服务器鉴别&#xff0c;允许用户证实服务器的身份。支持SSL的客户端通过验证来自服务器的证书&#xff0c;来鉴别…

Starfish 因子开发管理平台快速上手:如何完成策略编写与回测

DolphinDB 开发的因子开发管理平台 Starfish 围绕量化投研的因子、策略开发阶段设计&#xff0c;为用户提供了一个从数据管理、因子研究到策略回测的完整解决方案。 因子平台的回测引擎提供了多个关键的事件函数&#xff0c;涵盖策略初始化、每日盘前和盘后回调、逐笔、快照和…

排序算法(3)——归并排序、计数排序

目录 1. 归并排序 1.1 递归实现 1.2 非递归实现 1.3 归并排序特性总结 2. 计数排序 代码实现 3. 总结 1. 归并排序 基本思想&#xff1a; 归并排序&#xff08;merge sort&#xff09;是建立在归并操作上的一种有效的排序算法&#xff0c;该算法是采用分治法&#xff0…

GIN

gin是什么 Gin 是一个用 Go (Golang) 编写的 HTTP Web 框架。 它具有类似 Martini 的 API&#xff0c;但性能比 Martini 快 40 倍。如果你需要极好的性能&#xff0c;使用 Gin 吧。 特点&#xff1a;gin是golang的net/http库封装的web框架&#xff0c;api友好&#xff0c;注…

基于asp.net游乐园管理系统设计与实现

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm 等开发框架&#xff09; vue .net php python(flask Django) 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找…