文章目录

  • 一、priority_queue使用
    • 仿函数控制优先级
    • sort算法里的仿函数
  • 二、手撕优先级队列
    • 优先级队列的容器适配器
    • 入堆
    • 出堆
    • top/size/empty
    • 迭代器区间构造初始化(解耦)
  • 三、仿函数
    • 仿函数控制冒泡排序
    • 仿函数控制priority_queue
    • 比较逻辑仿函数使用场景
    • 仿函数的其他使用场景
  • 源码


一、priority_queue使用

在这里插入图片描述

priority_queue底层就是我们在数据结构初阶介绍的堆,只不过这里叠加了C++
的类、模板和我们下面要介绍的仿函数,并且它默认的结构是大根堆,使用测试如下:

在这里插入图片描述

仿函数控制优先级

我们要控制priority_queue的优先级需要用它的第三个模板参数,仿函数,有两个在库里的仿函数:
小于less仿函数<: 大的优先级高,排降序
大于greater仿函数>: 小的优先级高,排升序
我们会发现它的优先级逻辑和我们的理解是相反的,需要特别注意。

在这里插入图片描述

sort算法里的仿函数

在这里插入图片描述

之前在介绍sort排序算法时也提到了仿函数,它默认是排升序,要改变排序逻辑就要传不同的仿函数了,这里仿函数的逻辑就和我们想的一样了,less小于排升序,greater大于排降序。

在这里插入图片描述

还有一点需要注意,sort的仿函数传的是对象,所以后面要加(),相当于匿名函数,priority_queue传的是类型,所以不加括号。

在这里插入图片描述

二、手撕优先级队列

手撕优先级队列要用到很多在堆介绍的知识,如果有读者不太懂下面的操作建议去看看小编介绍的有关堆的文章:
数据结构与算法------堆(堆的实现、堆排序、TOP-K问题)

优先级队列的容器适配器

我们查文档可以看到优先级队列的容器适配器是vector,其实也可以用双端队列deque,但是用vector是最好的,因为优先级队列要频繁的下标随机访问,vector的效率更高,所以它的模板参数和成员函数为:

template<class T, class Container = vector<T>>Container _con;

入堆

//默认大堆
void Adjust_up(size_t child)
{size_t parent = (child - 1) / 2;while (child != 0){if (_con[child] > _con[parent]) {swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}void push(const T& x)
{_con.push_back(x);Adjust_up(_con.size() - 1);
}

这里和C语言实现堆的区别就是不用自己写数组的插入删除操作,因为容器适配器,所以直接复用vector的相关接口就行了。

出堆

void Adjust_down(size_t parent)
{size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && _con[child + 1] > _con[child])child++;if (_con[child] > _con[parent]){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void pop()
{swap(_con[0], _con[_con.size() - 1]);_con.pop_back();Adjust_down(0);
}

top/size/empty

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

迭代器区间构造初始化(解耦)

vector<int> v = { 4,1,3,6,7,2 };
wusaqi::priority_queue<int> pq(v.begin(), v.end());

我们想调用如上迭代器区间构造初始化就还需要我们自己实现一个对应的迭代器区间构造构造函数,我们实现了它的迭代器区间构造但是不想实现它的默认构造(不带参构造),就需要强制它生成默认构造,因为只要显示实现了一个构造函数编译器就不会自动生成默认构造了。
生成默认构造是为了初始化优先级队列的底层容器,因为默认构造会去调用自定类型的构造函数,当具体底层容器是什么取决于实例化对象时模板传的参数。这里的容器适配器可以体现一点,优先级队列底层存储数据的结构和堆算法本身是解耦的,在实际项目中,软件的耦合度一定是越低越好,耦合度低就意味着有更高的自由度。

		priority_queue() = default;

这里我们可以挨个遍历容器尾插到优先级队列中,但是这样就相当于向上调整建堆了,时间复杂度比较高,效率更高的方法还是用向下调整建堆,所以我们首先把这段迭代器区间的值拷贝一份给_con,方法是走初始化列表用_con提供的迭代器区间构造,然后再向下调整建堆。
这里要用到迭代器类型模板,让它能适配更多容器。

template<class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{auto it = first;while (it != last){push(*it);it++;}
}

上面是遍历迭代器区间并尾插,相当于向上调整算法建堆,效率不高,不推荐,下面的向下调整建堆才是最理想的方式。

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

三、仿函数

我们要明白,C++引入仿函数的目的是为了替代函数指针,因为函数指针用起来太复杂了,它的定义方式和常规的指针定义方式区别很大,用的时候很容易出问题。
函数指针还有一点缺陷,当函数指针类型做类模板参数时,无法直接创造出函数指针对象,因为指针类型实例化出的指针对象值是不确定的,不是存放指向函数对象的值,当然函数模板因为是传递对象就可以使用函数指针类型。

仿函数又名函数对象,它本质就是一个类,只不过重载了operator(),所以它定义出的对象可以像函数一样使用。(凡是重载了operator()的类都可以叫做仿函数)

在这里插入图片描述

再把它套上模板就可以支持任意类型数据比较了:

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

仿函数控制冒泡排序

下面我们用仿函数来控制冒泡排序排升序和降序:

template<class T, class compare>
void Bubble_sort(T* arr, int sz, compare cmp)
{int flag = 1;  //默认有序for (int i = 0; i < sz - 1; i++)  //躺数{for (int j = 0; j < sz - 1 - i; j++){if (cmp(arr[j + 1], arr[j])){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;flag = 0;}}if (flag == 1)break;}
}void test06()
{int arr[] = { 4,2,6,1,5,8,9,3,7 };  Bubble_sort(arr, 9, Less<int>()); for (auto ch : arr){cout << ch << " ";}cout << endl;Bubble_sort(arr, 9, Greater<int>());for (auto ch : arr){cout << ch << " ";}cout << endl;
}

这里要注意冒泡排序函数的模板是函数模板,不是类模板,需要通过调用函数传递对象参数来调用仿函数,不能像类模板那样:Bubble_sort<int,Less< int >> 只在模板参数这里传递仿函数类型,而不在函数参数哪里传递仿函数对象,如果非要这样传递,记住仿函数需要对象来调用,那需要在冒泡排序函数内调用仿函数时用匿名对象调用:compare()(arr[j], arr[j + 1])。

仿函数控制priority_queue

需要改动的就两个地方,一个是模板参数,多增加一个仿函数参数,因为默认排升序,建大堆,所以给一个缺省值类型Less< T> >。
还要把向上调整算法和向下调整算法的比较逻辑换成调用仿函数,这里和冒泡排序调用仿函数又有一点不同,冒泡排序是函数模板,调用仿函数是直接传递的仿函数对象,所以可以拿到参数直接使用,这里priority_queue是雷模板,仿函数传递过来的是仿函数类型,需要先实例化成对象后再使用,这里小编就不定义有名对象了,直接用匿名对象,少敲两行代码。

template<class T, class Container = vector<T>, class compare = Less<T>>void Adjust_up(size_t child){//compare cmp; size_t parent = (child - 1) / 2;while (child != 0){       //匿名对象if (compare()(_con[parent], _con[child])){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void Adjust_down(size_t parent){size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && compare()(_con[child], _con[child + 1]))child++;if (compare()(_con[parent], _con[child])){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}

比较逻辑仿函数使用场景

  • 类不支持比较大小
  • 类支持比较大小,但比较逻辑和预期不一致

这里小编补充一点,我们上面实现的仿函数都是空类(没有成员变量,对象空间大小为1字节),所以空类也是有使用场景的。

仿函数的其他使用场景

在算法库中也有一些函数会用到仿函数,下面以find_if为例简单介绍一下:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

我们查文档发现find是找到迭代器区间中第一个指向值为val的迭代器并返回,find_if是找到迭代器区间中第一个迭代器指向的值符合pred仿函数的值,并返回该迭代器,所以我们可以设计一个find_if用来查找第一个偶数:

class op1
{
public:bool operator()(int x){return (x % 2 == 0);}
};int arr[] = { 3,1,5,4,7,8,2 };auto it1 = find(arr, arr + 7, 1);cout << *it1 << endl;//查找第一个偶数auto it2 = find_if(arr, arr + 7, op1());cout << *it2 << endl;

运行结果:

在这里插入图片描述

源码

pq.h:

#pragma once
#include <algorithm>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 wusaqi
{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, Inpu tIterator last)//{//	auto it = first;//	while (it != last)//	{//		push(*it);//		it++;//	}//}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& x){_con.push_back(x);Adjust_up(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();Adjust_down(0);}T& top(){return _con[0];}bool empty(){return _con.empty();}size_t size(){return _con.size();}private://默认大堆void Adjust_up(size_t child){//compare cmp; size_t parent = (child - 1) / 2;while (child != 0){       //匿名对象if (compare()(_con[parent], _con[child])){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void Adjust_down(size_t parent){size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && compare()(_con[child], _con[child + 1]))child++;if (compare()(_con[parent], _con[child])){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}private:Container _con;};
}

test.cpp:

using namespace std;
#include <algorithm>
#include <iostream>
#include <queue>
#include "pq.h"void test01()
{wusaqi::priority_queue<int> pq;pq.push(5);pq.push(1);pq.push(3);pq.push(7);pq.push(2);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;
}void test02()
{////要传第三个模板参数必须要传第二个,不能跳跃传参//priority_queue<int, vector<int>, greater<int>> pq;  //pq.push(5);//pq.push(1);//pq.push(3);//pq.push(7);//pq.push(2);//while (!pq.empty())//{//	cout << pq.top() << " ";//	pq.pop();//}//cout << endl;vector<int> v1 = { 2,4,1,6,3,5 };sort(v1.begin(), v1.end());for (auto ch : v1){cout << ch << " ";}cout << endl;//sort(v1.begin(), v1.end(), greater<int>());for (auto ch : v1){cout << ch << " ";}cout << endl;
}void test03()
{vector<int> v = { 4,1,3,6,7,2 };wusaqi::priority_queue<int> pq(v.begin(), v.end());while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;
}class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};void test04()
{//有名对象A aa1;A aa2(66);//匿名对象A();A(88);
}void test05()
{Less<int> Funcless;cout << Funcless(1, 2) << endl; //Funcless.operator()(1,2)
}//template<class T, class compare> 
//void Bubble_sort(T* arr, int sz)
//{
//    int flag = 1;  //默认有序
//    for (int i = 0; i < sz - 1; i++)  //躺数
//    {
//        for (int j = 0; j < sz - 1 - i; j++)
//        {
//            if (compare()(arr[j], arr[j + 1]))
//            {
//                int tmp = arr[j];
//                arr[j] = arr[j + 1];
//                arr[j + 1] = tmp;
//                flag = 0;
//            }
//
//        }
//        if (flag == 1)
//            break;
//    }
//}template<class T, class compare>
void Bubble_sort(T* arr, int sz, compare cmp)
{int flag = 1;  //默认有序for (int i = 0; i < sz - 1; i++)  //躺数{for (int j = 0; j < sz - 1 - i; j++){if (cmp(arr[j + 1], arr[j])){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;flag = 0;}}if (flag == 1)break;}
}void test06()
{int arr[] = { 4,2,6,1,5,8,9,3,7 };Bubble_sort(arr, 9, Less<int>());for (auto ch : arr){cout << ch << " ";}cout << endl;Bubble_sort(arr, 9, Greater<int>());for (auto ch : arr){cout << ch << " ";}cout << endl;
}void test07()
{wusaqi::priority_queue<int, vector<int>, Greater<int>> pq;pq.push(5);pq.push(1);pq.push(3);pq.push(7);pq.push(2);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;
}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;
}class PDateLess
{
public:bool operator()(Date* pd1, Date* pd2){return (*pd1) < (*pd2);}
};class PDateGreater
{
public:bool operator()(Date* pd1, Date* pd2){return (*pd1) > (*pd2);}
};void test08()
{wusaqi::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;
}class op1
{
public:bool operator()(int x){return (x % 2 == 0);}
};void test09()
{int arr[] = { 3,1,5,4,7,8,2 };auto it1 = find(arr, arr + 7, 2);cout << *it1 << endl;//查找第一个偶数auto it2 = find_if(arr, arr + 7, op1());cout << *it2 << endl;
}int main()
{//test01();//test02();//test03();//test04();//test05();//test06();//test07();//test08();test09();return 0;
}

以上就是小编分享的全部内容了,如果觉得不错还请留下免费的赞和收藏
如果有建议欢迎通过评论区或私信留言,感谢您的大力支持。
一键三连好运连连哦~~

在这里插入图片描述

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

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

相关文章

在mac m1基于ollama运行deepseek r1

1 下载和安装 在ollama的官网下载mac m1版本的ollama https://ollama.com/ 最终获得如下所示的下载地址 https://github.com/ollama/ollama/releases/latest/download/Ollama.dmg 然后点击安装&#xff0c;然后测试 ollama list 2 运行deepseek r1 deepseek-r1:8b 比较适…

TCP与UDP协议详解:网络世界的可靠信使与高速快递

> 互联网的骨架由传输层协议支撑,而TCP与UDP如同血管中的红细胞与血小板,各司其职却又缺一不可 ### 一、初识传输层双雄:网络通信的基石 想象你要给朋友寄送重要文件: - **TCP** 如同顺丰快递:**签收确认+物流追踪**,确保文件完整送达 - **UDP** 如同普通信件:**直接…

Datawhale AI 夏令营【更新中】

Datawhale AI 夏令营【更新中】夏令营简介大模型技术&#xff08;文本&#xff09;方向&#xff1a;用AI做带货视频评论分析机器学习&#xff08;数据挖掘&#xff09;方向&#xff1a;用AI预测新增用户夏令营简介 本次AI夏令营是Datawhale在暑期发起的大规模AI学习活动&#…

AutoDL挂载阿里云OSS

文章目录前言AutoDL 设置阿里OSS设置OSS配置相关key 相关竞猜时间前言 最近&#xff0c;AutoDL提示北京A区网盘功能要下架&#xff0c;然后需要对网盘中数据进行转移等操作&#xff0c;我想网盘中数据下载到本地&#xff0c;大概16G&#xff1b;直接在网盘那里下载&#xff0c…

java 基本数据类型所对应的包装类

一,对应列举Java 中有 8 种基本数据类型&#xff0c;每种基本数据类型都有对应的包装类&#xff0c;它们分别是&#xff1a;二,包装类的作用1. 满足面向对象编程需求Java 是面向对象的编程语言&#xff0c;基本数据类型不是对象&#xff0c;无法使用面向对象的特性&#xff08;…

牛客网50题-10

1.小苯的数字权值#include <iostream> #include <algorithm> using namespace std;const int max_n 2000000; int d[max_n 1]; int f[max_n 1];int main() {for(int i 1; i<max_n;i){for(int j i; j<max_n;ji){d[j];}}for(int i1; i<max_n;i){f[i] d…

基于springboot的大学公文收发管理系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业多年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了多年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…

【机器学习】反向传播如何求梯度(公式推导)

写在前面 前期学习深度学习的时候&#xff0c;很多概念都是一笔带过&#xff0c;只是觉得它在一定程度上解释得通就行&#xff0c;但是在强化学习的过程中突然意识到&#xff0c;反向传播求梯度其实并不是一件简单的事情&#xff0c;这篇博客的目的就是要讲清楚反向传播是如何对…

ALB、NLB、CLB 负载均衡深度剖析

ALB、NLB、CLB 负载均衡深度剖析 前言 笔者在上周的实际工作中遇到了一个典型的负载均衡选择问题&#xff1a;在使用代理调用相关模型时&#xff0c;最初配置 Nginx 的代理地址为 ALB 的 7 层虚拟 IP&#xff08;VIP&#xff09;&#xff0c;但由于集团网络默认的超时时间为 3 …

历史数据分析——云南白药

医药板块走势分析: 从月线级别来看 2008年11月到2021年2月,月线上走出了两个震荡中枢的月线级别2085-20349的上涨段; 2021年2月到2024年9月,月线上走出了20349-6702的下跌段; 目前月线级别放巨量,总体还在震荡区间内,后续还有震荡和上涨的概率。 从周线级别来看 从…

【读书笔记】《Effective Modern C++》第3章 Moving to Modern C++

《Effective Modern C》第3章 Moving to Modern C 一、区分圆括号 () 与大括号 {} &#xff08;Item 7&#xff09; C11 引入统一初始化&#xff08;brace‑initialization&#xff09;&#xff0c;即使用 {} 来初始化对象&#xff0c;与传统的 () 存在细微差别&#xff1a;避…

Rust基础-part1

Rust基础[part1]—安装和编译 安装 ➜ rust curl --proto https --tlsv1.2 https://sh.rustup.rs -sSf | sh安装成功 [外链图片转存中…(img-ClSHJ4Op-1752058241580)] 验证 ➜ rust rustc --version zsh: command not found: rustc因为我是用的是zsh&#xff0c;所以zsh配置…

PyQt5布局管理(QGridLayout(网格布局))

QGridLayout&#xff08;网格布局&#xff09; QGridLayout&#xff08;网格布局&#xff09;是将窗口分隔成行和列的网格来进行排列。通常可以使用函数addWidget()将被管理的控件&#xff08;Widget)添加到窗口中&#xff0c;或者使用addLayout() 函数将布局&#xff08;Layou…

Java设计模式之行为型模式(责任链模式)介绍与说明

一、核心概念与定义 责任链模式是一种行为型设计模式&#xff0c;其核心思想是将请求沿着处理对象链传递&#xff0c;直到某个对象能够处理该请求为止。通过这种方式&#xff0c;解耦了请求的发送者与接收者&#xff0c;使多个对象有机会处理同一请求。 关键特点&#xff1a; 动…

SQL server之版本的初认知

SQL server之版本的初认知 为什么要编写此篇文档呢&#xff0c;主要是因为在最近测试OGG实时同步SQL server数据库表数据的时候&#xff0c;经过多次测试&#xff0c;发现在安装了一套SQL server2017初始版本&#xff0c;未安装任何补丁的时候&#xff0c;在添加TRANDATA的时候…

【前端】jQuery动态加载CSS方法总结

在jQuery 中动态加载 CSS 文件有多种方法&#xff0c;以下是几种常用实现方式&#xff1a; 方法 1&#xff1a;创建 <link> 标签&#xff08;推荐&#xff09; // 动态加载外部 CSS 文件 function loadCSS(url) {$(<link>, {rel: stylesheet,type: text/css,href:…

Python爬虫实战:研究xlwings库相关技术

1. 引言 在金融科技快速发展的背景下,数据驱动决策已成为投资领域的核心竞争力。金融市场数据具有海量、多源、实时性强等特点,传统人工收集与分析方式难以满足高效决策需求。Python 凭借其丰富的开源库生态,成为金融数据分析的首选语言。结合 Requests、BeautifulSoup 等爬…

Linux 内核日志中常见错误

目录 **1. `Oops`****含义****典型日志****可能原因****处理建议****2. `panic`****含义****典型日志****可能原因****处理建议****3. `BUG`****含义****典型日志****可能原因****处理建议****4. `kernel NULL pointer`****含义****典型日志****可能原因****处理建议****5. `WA…

Linux驱动开发2:字符设备驱动

Linux驱动开发2&#xff1a;字符设备驱动 字符设备驱动开发流程 字符设备是 Linux 驱动中最基本的一类设备驱动&#xff0c;字符设备就是一个一个字节&#xff0c;按照字节流进行读写操作的设备&#xff0c;读写数据是分先后顺序的。比如最常见的点灯、按键、 IIC、 SPI&#x…

RuoYi-Cloud 验证码处理流程

以该处理流程去拓展其他功能模块处理流程&#xff0c;进而熟悉项目开发代码一、思路JavaWeb流程主干线&#xff1a;发起请求、处理请求、响应请求二、登录页面在登录页面按键F12打开开发者工具&#xff0c;点击network&#xff0c;刷新页面&#xff0c;点击code&#xff0c;查看…