【玩转c++】priority_queue的介绍和模拟实现

news2025/3/2 1:22:15

本期主题:priority_queue的介绍和模拟实现

博客主页: 小峰同学

分享小编的在Linux中学习到的知识和遇到的问题

小编的能力有限,出现错误希望大家不吝赐

  1. priority_queue介绍和使用

1.1.priority_queue介绍

1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
2. 类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素 (优先队列中位于顶部的元 素)。
3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特 定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
4. 底层容器可以是任何标准容器类模板, 也可以是其他特定设计的容器类。容器应该可以通过随机访问迭 代器访问, 并支持以下操作: empty():检测容器是否为空 size():返回容器中有效元素个数 front():返回容器中第一个元素的引用 push_back():在容器尾部插入元素
pop_back():删除容器尾部元素
5. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指 定容器类,则使用vector,一般不会使用deque,因为deque的随机访问,效率不高.建堆效率很低.
6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数 make_heap、push_heap和pop_heap来自动完成此操作。

1.2.priority_queue使用

priority_queue的接口很简单,但是他的结构很厉害.
比如在使用堆排序,topK 问题的时候 ,是无可替代的.
  1. 模拟实现

直接上源码
namespace zxf
{

    //仿函数
    template<class T>
    struct lass
    {
        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;
        }
    };


    //优先级队列其实就是一个堆
    //底层也是一种适配器模式,可以使用 vector 或者 deque 来适配。
    //priority_queue的底层数据结构必须满足可以随机访问,
    //所以 只有 vector 和 deque,满足。
    template<class T,class container = vector<T>, class compare = lass<T>>//默认使用 lass 建立大堆
    class priority_queue
    {
        container _con;
    public:
        //向上调整
        void adjust_up(size_t child)
        {
            compare com;
            size_t parent;
            while (child > 0){//注意这里 child 不能等于 0 
                parent = (child - 1) / 2;
                if (com( _con[parent] ,_con[child])){
                    std::swap(_con[child], _con[parent]);
                    child = parent;
                }
                else{
                    break;
                }
            }
        }

        //向下调整
        void adjust_down(size_t parent)
        {
            //parent*2+1=left_child
            //parent*2+2=right_child
            compare com;
    
            size_t child = parent * 2 + 1;
            while (child < _con.size())
            {
                if (child + 1 < _con.size() && _con[child + 1] > _con[child])
                {
                    child++;
                }
                if (com(_con[parent], _con[child]))
                {
                    swap(_con[child], _con[parent]);
                    parent = child;
                    child = parent * 2 + 1;
                }
                else{
                    break;
                }
            }
        }

        priority_queue() {}

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

            //也可以
            //_con(first, last);
            //(child-1) / 2 = parent
            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);
        }

        bool empty()
        {
            return _con.empty();
        }
        size_t size()
        {
            return _con.size();
        }

        //堆顶的元素不允许修改,因为修改可能会导致结构破坏。
        const T& top()const
        {
            return _con[0];
        }
    };
}
  1. 常见问题

3.1.仿函数

仿函数,顾名思义就是仿照的函数.通常情况下会
//仿函数
//其实是一个类
//他的实例化出来的对象,可以像函数一样去使用。
namespace zxf
{
    template<class T>
    struct lass
    {
        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;
        }
    };
}
这个仿函数(类),实例化出来的对象可以像函数一样去使用
int main()
{
    //需要先给模板显式实例化一下。
    //greater是类名(类模板),greater<int> 是一个类型。
    zxf::greater<int> greaterfunc;
    //greaterfunc 是类型实例化出来的一个对象。其实和 vector<int> v; 相同。
    zxf::lass<int> lassfunc;

    //只不过这个自定义类型里面没有成员变量,是有一个函数调用访问符() 的重载。
    cout << greaterfunc(2, 1) << endl;//1
    cout << greaterfunc.operator()(2, 1) << endl;//1
    
    cout << lassfunc(2, 5) << endl;//1
    cout << lassfunc.operator()(2, 5) << endl;//1


    //这里不是
    return 0;
}
在priority_qeueue底层实现的时候就用到了仿函数 lass 和 greater 来控制建立大堆还是小堆.

3.2.如果在priority_queue中放自定义类型的数据

用户需要在自定义类型中提供> 或者< 的重载。
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));
     cout << q1.top() << 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));
     cout << q2.top() << endl;
}
这里重载> 和 < 是为了供 函数greater或者lass 里面调用的.

注意 :
我们知道假如堆里面的每一个元素是自定义类型,我们只需要在类里面 重载 < 和> 即可.
但是如果是一个自定义的指针我们怎么办.
假如里面模板实例化的类型是Date* (自定义类型的指针) 的类型怎么办.
这个也简单,就是默认使用的是库里面的仿函数lass 和 greater ,底层就是通过 运算符< 和> 来比较的,但是我们自己写一个仿函数传给他,按照我们自己的意愿来比较就可以.

template <class T>
struct Date_lass
{
    bool operator()(const T& t1 , const T& t2)
    {
        return (*t1) < (*t2);//直接复用Date类里面的重载即可。

    }
};

template <class T>
struct Date_greater
{
    bool operator()(const T& t1, const T& t2)
    {
        return (*t1) > (*t2);//直接复用Date类里面的重载即可。

    }
};

void TestPriorityQueue()
{
    //我们可以看到这里每个堆元素是 Date* (日期类的指针)
    //如果我们还继续使用 库函数给我提供的 ;lass 和 greater 仿函数
    //直接就是地址相比。不是我们想要的效果。
    zxf:: priority_queue<Date*, vector<Date*>, Date_lass<Date*>> q1;//建立大堆
    q1.push(new Date(2018, 10, 29));
    q1.push(new Date(2018, 10, 28));
    q1.push(new Date(2018, 10, 30));
    cout << *(q1.top()) << endl;

    zxf::priority_queue<Date*, vector<Date*>, Date_greater<Date*>> q2;//建立小堆
    q2.push(new Date(2018, 10, 29));
    q2.push(new Date(2018, 10, 28));
    q2.push(new Date(2018, 10, 30));
    cout << *(q2.top()) << endl;
}


int main()
{
    TestPriorityQueue();
    return 0;
}

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

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

相关文章

数组边遍历(for循环)边删除为什么删不干净 及三种实现删除的方法

文章目录1、为什么删不干净倒序删迭代器lambda表达式删除为什么说数组边for循环遍历边删除会出现删不干净的情况1、为什么删不干净 先写一个例子&#xff1a;可以先猜一下控制台会打印出什么内容&#xff1f; public class removeIterator {public static void main(String[]…

Node.js 里 Express工程框架中的代码解析

新建一个工程 express -e blog配置入口文件 打开项目内的app.js ,在倒数第二行写入如下代码&#xff1a; app.listen(3000, function () {console.log(server is running in 3000) })这样就让服务运行在了3000端口 检查 打开终端输入&#xff1a; node app在打开浏览器访…

client-go实战之十:标签选择(labels.Selector),重要

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 系列文章链接 client-go实战之一&#xff1a;准备工作client-go实战之二:RESTClientclient-go实战之三&#xff1a;Clientsetclient-go实战之四&#xff1a;…

智能优化算法求解CEC2005中F1-F25的种群动态变化图(视频)

CEC2005 测试集共包含 25 个测试问题, 即 f1−f25f_{1}-f_{25}f1​−f25​ 。根据问题的特征, 可进一 步将其分为 4 类: 单峰问题 f1−f5f_{1}-f_{5}f1​−f5​ , 基本多峰问题 f6−f12f_{6}-f_{12}f6​−f12​ , 扩展多峰问题 f13−f14f_{13}-f_{14}f13​−f14​ 和混合复合问…

STL源码学习(1)-空间配置器

文章目录1. SGI空间配置器1.1 std::allocator1.2 std::alloc1.3 对象构造与析构&#xff1a;construct和destroy1.4 空间配置与释放&#xff1a;allocate和deallocate1.4.1 第一级空间配置器1.4.2 第二级空间配置器1.4.2.1 空间配置函数 allocate1.4.2.2 空间释放函数 dealloca…

C++回顾(二十二)—— stack容器 与 queue容器

22.1 stack容器 &#xff08;1&#xff09; stack容器简介 stack是堆栈容器&#xff0c;是一种“先进后出”的容器。stack是简单地装饰deque容器而成为另外的一种容器。添加头文件&#xff1a;#include <stack> &#xff08;2&#xff09;stack对象的默认构造 stack…

Web漏洞-XXE漏洞(详细)

XXE漏洞XXE全称为XML External Entity Injection即XMl外部实体注入漏洞原理&#xff1a;XXE漏洞发生在应用程序解析XML输入时&#xff0c;没有禁止外部实体的加载&#xff0c; 导致用户可以控制外部的加载文件&#xff0c;造成XXE漏洞。XXE漏洞触发点往往是可以上传xml文件的位…

行测-判断推理-类比推理-逻辑关系-交叉关系

花瓶和瓷器是交叉关系&#xff0c;除了瓷花瓶&#xff0c;还有塑料花瓶等花瓶中有一部分是瓷器&#xff0c;瓷器中有一部分是花瓶A选项&#xff1a;电视机是电器的一种&#xff0c;种属关系&#xff0c;排除AB选项&#xff1a;中药和植物是交叉关系&#xff0c;还有动物入药等&…

< 开源项目框架:推荐几个开箱即用的开源管理系统 - 让开发不再复杂 >

文章目录&#x1f449; SCUI Admin 中后台前端解决方案&#x1f449; Vue .NetCore 前后端分离的快速发开框架&#x1f449; next-admin 适配移动端、pc的后台模板&#x1f449; django-vue-admin-pro 快速开发平台&#x1f449; Admin.NET 通用管理平台&#x1f449; RuoYi 若…

SpringBoot 整合 Activiti7

一.pom依赖引入 通过该 pom.xml 文件所导入的坐标&#xff0c;我们就可以实现 activiti7 与 Springboot 整合 <!--activiti7与SpringBoot整合的相关依赖--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-start…

【教学典型案例】04.生产环境app打包导致不能自动升级

目录一&#xff1a;背景介绍现象原因产生影响二&#xff1a;问题复现过程1、注册服务空间2、添加Admin项目3、创建uniapp项目4、App打包5、发布到升级中心三&#xff1a;总结一&#xff1a;背景介绍 现象 用户端安装了74版本的app&#xff0c;不能自动升级。 原因 产生该问…

win10 +cuda11.3.1+VS2019-社区版+drive445.87

参考&#xff1a;https://blog.csdn.net/kunhe0512/article/details/124331221这里的推荐离线安装包的方式进行cuda安装&#xff0c;官方也给了conda的安装方式&#xff0c;我试过一直出问题&#xff0c;所以安装包的方式比较靠谱&#xff0c;windows比linux下更方便。vs2019-c…

Uipath Excel 自动化系列20-Insert Chart(插入图表)

活动描述 Insert Chart(插入图表)&#xff1a;在Excel工作表的指定位置插入图表&#xff0c;该活动需与Use Excel File 活动选择的 Excel 文件一起使用。 使用如下图&#xff1a; Insert Chart(插入图表)属性配置 属性 作用 DisplayName 在设计器面板中设置活动显示的名称…

计算机网络笔记——计算机网络体系结构

计算机网络笔记——计算机网络体系结构1.计算机网络体系结构1.1 计算机网络概述1.1.1 计算机网络的概念1.1.2 计算机网络组成物理组成工作方式组成功能组成1.1.3 计算机网路的功能1.1.4 计算机网络的分类1.1.5 计算机网络的标准化公工作及相关组织1.2 计算机网络体系结构与参考…

Python调用Shell命令 (python,shell 混合编程)

Python经常被称作“胶水语言”&#xff0c;因为它能够轻易地操作其他程序&#xff0c;轻易地包装使用其他语言编写的库&#xff0c;也当然可以用Python调用Shell命令。 用Python调用Shell命令有如下几种方式&#xff1a; 1. os.system os.system("The command you want&…

fiddler

文章目录fiddler简介URL与HTTPURLhttp模拟测试场景弱网测试自定义规则前端性能分析及优化fiddler 简介 Fiddler是位于客户端和服务端的HTTP代理 目前最常用的http抓包工具之一功能非常强大&#xff0c;是web调试的利器 监控浏览器所有的HTTP/HTTPS流量查看、分析请求内容细节伪…

Android之屏幕适配方案

在说明适配方案之前&#xff0c;我们需要对如下几个概念有所了解&#xff1a;屏幕尺寸&#xff0c;屏幕分辨率&#xff0c;屏幕像素密度。 屏幕尺寸 屏幕尺寸指屏幕的对角线的物理长度&#xff0c;单位是英寸&#xff0c;1英寸2.54厘米。 比如常见的屏幕尺寸&#xff1a;5.0、5…

粒子群优化SVM含水率预测

目录 支持向量机SVM的详细原理 SVM的定义 SVM理论 SVM应用实例,基于粒子群改进SVM的含水率回归分析 代码 结果分析 展望 支持向量机SVM的详细原理 SVM的定义 支持向量机(support vector machines, SVM)是一种二分类模型,它的基本模型是定义在特征空间上的间隔最大的线性…

python --生成时间序列,作为横轴的标签。时间跨越2008-2022年,生成每年的6-10月的第一天作为时间序列

python 生成制定的时间序列作为绘图时x轴的标签 问题需求 在绘图时&#xff0c;需要对于x轴的标签进行专门的设置&#xff0c;整体时间跨越2008年-2022年&#xff0c;将每年的6-10月的第一天生成一条时间序列&#xff0c;绘制成图。 解决思路 对于时间序列的生成&#xff0…

【react 全家桶】条件渲染

文章目录05 【条件渲染】基础配置1.条件判断语句2.三目运算符3.与运算符&&4.元素变量5.阻止组件渲染05 【条件渲染】 在 React 中&#xff0c;你可以创建不同的组件来封装各种你需要的行为。然后&#xff0c;依据应用的不同状态&#xff0c;你可以只渲染对应状态下的部…