【STL源码剖析】priority_queue 优先队列的简单实现

水到绝处是风景

人到绝境是重生


目录

priority_queue的模拟实现 

源码剖析:

代码测试:

 契子✨ 


我们之前不仅讲过 队列queue 还有 双端队列deque 而我们今天所讲的依旧是队列家族的成员 -- 优先队列priority_queue

顾名思义,priority_queue是一个拥有权值观念的 queue,它允许增删元素、访问元素等功能。由于这是一个 queue,所以只允许在低端加入元素,并从顶端取出元素,除此之外别无其他存取元素的途径

priority_queue 带有权值观念,其内的元素并非依照推入的顺序排序,而是自动依照元素的权值排序(权值通常以实值表示)。权值最高者,排在前面

大家想象一下,我们之前学过的数据结构有哪一种具有类似的性质?

是不是像我们学过的 -- 堆(heap),我们可以利用 heap 的特性完成 [依权值高低自动递增排序] priority_queue

优先队列 priority_queue 是一种容器适配器,默认使用 vector 作为其底层存储数据的容器,在 vector 上又使用了堆算法将 vector 中元素构造成堆的结构,因此 priority_queue 就是堆,所有需要用到堆的位置,都可以考虑使用 priority_queue。注意: 默认情况下 priority_queue 是 大堆

 priority_queue 没有迭代器

priority_queue 的所有元素,进出都有一定的规则,只有 queue 的顶端元素(权值最高元素),才有机会被外界取用,priority_queue 不提供遍历功能,也不提供迭代器功能


priority_queue的模拟实现 

通过对 priority_queue 的底层结构默认就是 vector ,然后我们处理一下形成堆,因此此处只需对对进行通用的封装即可。操作非常简单,源码很简短,这里就完整的列出吧 ~ 然后在讲一下细节

#include<vector>
#include<iostream>
using std::vector;
using std::swap;namespace Mack
{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;}};template<class T,class Sequence = vector<T>, class Comapre = less<T> >class priority_queue{public:priority_queue() = default;template <class InputIterator>priority_queue(InputIterator first, InputIterator last){while (first != last){c.push_back(*first);++first;}for (int i = (c.size() - 1 - 1) / 2; i >= 0; i--){AdjustDown(i);}}const T& top() const{return c.front();}bool empty() const{return c.empty();}size_t size() const{return c.size();}void AdjustUP(size_t child){size_t parent = (child - 1) / 2;while (child > 0){if (comp(c[parent] , c[child])){swap(c[parent], c[child]);child = parent;parent = (child - 1) / 2;}else{break;}}}void push(const T& val){c.push_back(val);AdjustUP(size()-1);}void AdjustDown(size_t parent){size_t child = parent * 2 + 1;while (child < size()){while (child + 1 < size() && comp(c[child] , c[child+1])){child++;}if (comp(c[parent] , c[child])){swap(c[parent], c[child]);parent = child;child = parent * 2 + 1;}else{break;}}}void pop(){swap(c[0], c[size() - 1]);c.pop_back();AdjustDown(0);}private:Sequence c;Comapre comp;};}

我们先来分析一下库里面的优先队列

对啦 ~ 头文件依然是 #include<queue>

源码剖析:

#include<queue>
#include<iostream>
using namespace std;
void priority_queue_test()
{priority_queue<int>  str;str.push(10);str.push(30);str.push(20);str.push(50);str.push(35);while (!str.empty()){cout << str.top() << " ";str.pop();}
}

我们发现库里的优先队列默认排的是降序也就是大堆 ~ 

所以我们写优先队列时,也要按照大堆的方式去写

关于算法,老铁们可以借鉴一下这个:二叉堆

我们重点讲一下关于 priority_queue 的自动排序,我们知道我们现在的优先队列排的是降序,那我们想排升序怎么办呢?难道要将堆中的比较符号都改一下吗?

我们先来看一下库里的算法:

#include<queue>
#include<iostream>
using namespace std;void priority_queue_test()
{priority_queue<int, vector<int>,greater<int>>  str;str.push(10);str.push(30);str.push(20);str.push(50);str.push(35);while (!str.empty()){cout << str.top() << " ";str.pop();}
}

用惯排序 sort 的老铁可能会有些不习惯,为什么中间还要加一个参数,因为库里就是以下的格式,就跟传缺省一样不能隔代相传

template<class T,class Sequence = vector<T>, class Comapre = less<T> >

我们回到重点!!!

在我们我们 C语言 阶段的话频繁的比较大小我们一般都会写成一个函数 

bool Compare(int x, int y)
{return x < y;
}int main()
{int x = 0, y = 1;if (Compare(x, y)){printf("y>x");}else{printf("y<x");}return 0;
}

如果比较 int 我们写一个专门比较 int 类型的函数,char 类型则专门写一个char 类型的函数

当我们学了 C++ 就开摆了,编程的进步就是变懒的过程 -- 我们可以利用模板来控制类型的比较

而要使用模板的前提必须是一个类,或者类中的函数

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

所以我们用一个类去包含比较函数在利用模板,而我们重载()的原因就是想写成这样一种函数的形式:Compare(x, y) -- 这样方便比较

我们调用类中的函数是不是都是 类对象+点运算符,我们将()重载便可以写成函数的形式

这样的函数形式我们称之为伪函数

为了让我们得初始化方便,库里提供了迭代器区间构造

有些老铁可能会疑惑,不是不提供迭代器吗,怎么还会有迭代器区间构造?
嘿嘿 ~ 其实我们的数组也可以进行迭代

		template <class InputIterator>priority_queue(InputIterator first, InputIterator last){while (first != last){c.push_back(*first);++first;}for (int i = (c.size() - 1 - 1) / 2; i >= 0; i--){AdjustDown(i);}}

先将数据尾插到对象中,在向下调整建堆,因为向下调整要找到第一个非叶子节点  

这里放张动图以便老铁理解:

(_con.size() - 1 - 1) / 2 的 _con.size() - 1 是找到最后一个节点,(_con.size() - 1 - 1) / 2,则是套公式 parent  = (child-1) /2 找到最后一个节点的双亲也就是第一个非叶子节点


代码测试:

别的不说先来测试一下代码,不要哔哔了一大段文字结果代码都是错的

void priority_queue_test()
{int arr[] = {1,3,5,7,9,2,4,6,8,0};priority_queue<int, vector<int>> str(arr, arr + 9);while (!str.empty()){std::cout << str.top() << " ";str.pop();}
}

 

#include"priority_queue.h"
#include<iostream>
using std::iostream;
using std::ostream;
using namespace Mack;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){_cout << d._year << "-" << d._month << "-" << d._day;return _cout;}
private:int _year;int _month;int _day;
};void TestPriorityQueue()
{priority_queue<Date> q1;q1.push(Date(2018, 10, 29));q1.push(Date(2018, 10, 28));q1.push(Date(2018, 10, 30));std::cout << q1.top() << std::endl;priority_queue<Date, vector<Date>, greater<Date>> q2;q2.push(Date(2018, 10, 29));q2.push(Date(2018, 10, 28));q2.push(Date(2018, 10, 30));std::cout << q2.top() << std::endl;
}int main()
{	TestPriorityQueue();std::cout << std::endl;system("pause");return 0;
}


 

 有问题的话可以提出来哦 ~ 

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

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

相关文章

空间搜索geohash概述

概述 通常在一些2C业务场景中会根据用户的位置来搜索一些内容。通常提供位置搜索的都是直接通过redis/mongodb/es等中间件实现的。 但是这些中间件又是怎么实现位置搜索的呢&#xff1b; 查了一番资料&#xff0c;发现背后一个公共的算法Geohash。 Geohash 经度和纬度是2个…

Amesim示例篇-案例2:液体循环回路

前文已完成流体库常用的元件参数与使用方法简单的介绍。本文将对液体回路系统管路的压降标定仿真方法与注意事项进行讨论。首先&#xff0c;本案例应用到的元件有膨胀水壶、水泵、阻力管、常规管路等元件。将上述元件进行串联组成液冷循环回路。 图1 膨胀水壶 图2 水泵 1…

如何让tracert命令的显示信息显示*星号

tracert命令如果在中间某一个节点超时&#xff0c;只会在显示信息中标识此节点信息超时“ * * * ”&#xff0c;不影响整个tracert命令操作。 如上图所示&#xff0c;在DeviceA上执行tracert 10.1.2.2命令&#xff0c;缺省情况下&#xff0c;DeviceA上的显示信息为&#xff1a;…

拼接屏处理器

拼接屏系统由三大部分组成&#xff0c;即拼接墙、液晶拼接处理器和信号源。其中液晶拼接处理器是关键技术的核心&#xff0c;支持不同像素的图像在大屏显示墙上显示以及在大屏显示墙上任意开窗口、BSV画面叠加、窗口放大缩小、跨屏漫游显示等。液晶拼接处理器一般分为两种&…

C++ AVL树 详细讲解

目录 一、AVL树的概念 二、AVL树的实现 1.AVL树节点的定义 2.AVL树的插入 3.AVL树的旋转 4.AVL树的验证 三、AVL树的性能 四、完结撒❀ 一、AVL树的概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但 如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查 …

Redis限流方案

限流简介 限流算法在分布式领域是一个经常被提起的话题&#xff0c;当系统的处理能力有限时&#xff0c;如何阻止计划外的请求继续对系统施压&#xff0c;是一个需要重视的问题。 除了控制流量&#xff0c;限流还有一个应用目的是用于控制用户行为&#xff0c;避免垃圾请求&a…

企业公户验证API在Java、Python、PHP中的使用教程

在金融和商业领域&#xff0c;企业公户验证API是一种用于验证企业对公账户的真实性和合法性的技术解决方案。这种API通常由金融机构或第三方服务提供商提供&#xff0c;旨在帮助企业加快账户认证流程&#xff0c;提高效率&#xff0c;降低审核成本&#xff0c;并确保符合法规要…

苹果Safari怎么清理缓存?原来快速清除浏览器的历史记录那么容易

在数字化时代&#xff0c;互联网已经成为我们日常生活中不可或缺的一部分。我们使用各种设备&#xff0c;如智能手机、平板电脑和笔记本电脑来浏览网页、获取信息、娱乐和社交。而在这些设备中&#xff0c;iPhone无疑是最受欢迎的选择之一。iPhone搭载的Safari浏览器以其简洁的…

ui自动化中,鼠标操作

from selenium.webdriver import ActionChainsaction ActionChains(driver) # 然后把driver作为参数&#xff0c;实例化一个action对象 练习地址&#xff1a;https://sahitest.com/demo/ 悬停例子&#xff08;百度首页&#xff09; action.move_to_element(ele).perfor…

Springboot注意点

1.Usermapper里加param注解 2.RequestParam 和 RequestBody的区别&#xff1a; RequestParam 和 RequestBody的区别&#xff1a; RequestParam 和 RequestBody 是Spring框架中用于处理HTTP请求的两个不同的注 get请求一般用url传参数&#xff0c;所以参数名和参数的值就在ur…

Type-C音频转接器方案

在数字化时代&#xff0c;音频设备作为我们生活中不可或缺的一部分&#xff0c;其连接方式的便捷性和高效性显得尤为重要。Type-C音频转接器&#xff0c;作为一种新型的音频连接解决方案&#xff0c;正逐渐走进我们的生活&#xff0c;以其独特的优势改变着我们的音频体验。 一、…

Vue Router 4与路由管理实战

title: Vue Router 4与路由管理实战 date: 2024/6/7 updated: 2024/6/7 excerpt: 这篇文章介绍了如何在Vue.js应用中利用Vue Router实现单页面应用的路由管理&#xff0c;包括配置路由、导航守卫的使用、路由懒加载以优化性能以及动态路由的实现方法&#xff0c;旨在提升用户体…

xiaolingcoding 图解网络笔记——基础篇

文章目录 参考一、网络模型有哪几层DMANAPI 机制二、键入网址到网页显示&#xff0c;期间发生了什么&#xff1f;1. HTTP2. DNS3. 协议栈4. TCP5. IP6. MAC7. 网卡8. 交换机9. 路由器10. 服务器 与 客户端的互相扒皮&#xff08;添加、删除头部信息&#xff09;参考图HTTP 请求…

Vue3【六】setup的使用和setup的返回值

Vue3【六】setup的使用和setup的返回值 setup函数的使用&#xff0c;和vue2的选项式不同 vue3的组合式使用的是setup函数 通过返回值将数据和方法传到页面 返回值也可以是一个箭头函数 setup先于 data和method执行所有无法读取到this和data&#xff0c;method的内容&#xff0c…

顶顶通呼叫中心中间件-asr录音路径修改(mod_cti基于FreeSWITCH)

顶顶通呼叫中心中间件-asr录音路径修改(mod_cti基于FreeSWITCH) 录音路径模板。如果不是绝对路径&#xff0c;会把这个路径追加到FreeSWITCH的recordings后面。支持变量&#xff0c;比如日期 ${strftime(%Y-%m-%d)}。最后一个录音文件路径会保存到变量 ${cti_asr_last_record_…

C语言详解(动态内存管理)1

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

Django 传递额外参数给视图函数

本书1-7章样章及配套资源下载链接: https://pan.baidu.com/s/1OGmhHxEMf2ZdozkUnDkAkA?pwdnanc 源码、PPT课件、教学视频等&#xff0c;可以从前言给出的下载信息下载&#xff0c;大家可以评估一下。 在Django框架中&#xff0c;URLconf模块还支持一种传递额外参数给视图函…

2024-06-07 Unity 编辑器开发之编辑器拓展8 —— Scene 窗口拓展

文章目录 1 Handles 类1.1 Scene 响应函数1.2 自定义窗口中监听 Scene1.3 Handles 常用 API2.2.1 颜色控制2.2.2 文本2.2.3 线段2.2.4 虚线2.2.5 圆弧2.2.6 圆2.2.7 立方体2.2.8 几何体2.2.9 移动、旋转、缩放2.2.10 自由移动 / 旋转 2 Scene 窗口中显示 GUI3 HandleUtility4 G…

Python 将CSV文件转为PDF文件

CSV文件通常用于存储大量的数据&#xff0c;而PDF文件则是一种通用的文档格式&#xff0c;便于与他人共享和打印。将CSV文件转换成PDF文件可以帮助我们更好地管理和展示数据。本文将介绍如何通过Python编程将CSV文件导出为PDF文件。 Python Excel库安装及介绍 在 Python 中&am…

GIGE 协议摘录 —— GVSP 协议(三)

系列文章目录 GIGE 学习笔记 GIGE 协议摘录 —— 设备发现&#xff08;一&#xff09; GIGE 协议摘录 —— GVCP 协议&#xff08;二&#xff09; GIGE 协议摘录 —— GVSP 协议&#xff08;三&#xff09; GIGE 协议摘录 —— 引导寄存器&#xff08;四&#xff09; GIGE 协议…