priority_queue的模拟实现和仿函数

news2024/11/25 20:33:35
priority_queue模拟
首先查看源代码,源代码就在queue剩下的部分中
push_heap是STL库中的堆算法,STL库中包装有支持堆的算法,在algorithm.h中:
只要不断用堆的形式插入数据,就会形成堆。
priority_queue模拟——初版
priority_queue.h中
#pragma once
#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;
template<class T, class container = vector<int>>
class myPriority_queue
{
public:
        void adjustUp(int lea) //向上调整算法,在插入数据后,将数据重新调整为堆时使用,lea为尾节点下标
        {
               if (lea == 0)
                       return;
               size_t fa = (lea - 1) / 2;
               while (_con[fa] < _con[lea])
               {
                       swap(_con[fa], _con[lea]);
                       lea = fa;
                       fa = (lea - 1) / 2;
               }
        }
        void adjustDown() //向下调整算法,在删除数据后,使空间重新调整为堆时使用,从根节点开始调整,不需要传入数据
        {
               size_t ch = 1; //代表子节点
               size_t fa = 0; //代表父节点
               while (ch < _con.size() && _con[fa] < _con[ch])
               {
                       if (ch + 1 < _con.size() && _con[ch] < _con[ch + 1]) //开始默认左子节点大
                              ch++;
                       swap(_con[fa], _con[ch]);
                       fa = ch;
                       ch = fa * 2 + 1;
               }
        }
        void push(const T& x)
        {
               _con.push_back(x);
               int lea = _con.size() - 1;
               adjustUp(lea);
        }
        void pop()
        {
               assert(!_con.empty());
               size_t tail = _con.size() - 1;
               swap(_con[0], _con[tail]);
               _con.pop_back();
               adjustDown();
        }
        const T& top()
        {
               return _con[0];
        }
        size_t size()
        {
               return _con.size();
        }
        bool empty()
        {
               return _con.empty();
        }
private:
        container _con;
};
test.cpp中
#define _CRT_SECURE_NO_WARNINGS 1
#include"priority_queue.h"
#include<iostream>
#include<vector>
using namespace std;
void test_priority_queue()
{
        myPriority_queue<int> pri_qu;
        pri_qu.push(8);
        pri_qu.push(2);
        pri_qu.push(5);
        pri_qu.push(1);
        pri_qu.push(4);
        pri_qu.push(3);
        cout << pri_qu.empty() << " " << pri_qu.size() << endl;
        while(!pri_qu.empty())
        {
               cout << pri_qu.top() << " ";
               pri_qu.pop();
        }
        cout << endl;
}
int main()
{
        test_priority_queue();
        return 0;
}
这样写出来的优先级队列只能排升序,而实际上的优先级队列是可以排升序,也可以排降序的。
实现升序降序控制的叫仿函数,即一开始priority_queue源代码看到的第三个模板参数。
所谓仿函数函数实际上是一个对象,在对象中重载了 '()' 符号,已知 '()' 符号有两种用法,一是(表达式),可以用于数据类型强转,或者控制优先级;一是作函数调用,这里重载的就是函数调用,将类写成一个类似函数的使用方式,所以叫仿函数。也叫函数对象。
下面写一个简单的仿函数:
class less
{
    bool operator()(int x, int y)
    {
        return x < y;
    }
};
使用这个类:
less  l1;
bool ret = l1(10, 20)
将仿函数修改为泛型:
template<class T>
class less
{
    bool operator()(const T& x, const T& y) const
    {
        return x < y; //支持的前提条件是类型要支持用 < 符号来进行大小的比较
    }
};
仿函数是一个类,是类就可以作为参数传递。
priority_queue模拟——进阶版(只是添加上了仿函数)
priority_queue.h中
#pragma once
#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;
template<class T>
class myless
{
public:
        bool operator()(const T& x, const T& y) const
        {
               return x < y;
        }
};
template<class T>
class mygreater
{
public:
        bool operator()(const T& x, const T& y) const
        {
               return x > y;
        }
};
template<class T, class container = vector<int>, class compare = myless<T>> //和库中一样,默认为大堆
class myPriority_queue
{
public:
        void adjustUp(int lea) //向上调整算法,在插入数据后,将数据重新调整为堆时使用,lea为尾节点下标
        {
               compare func;
               if (lea == 0)
                       return;
               size_t fa = (lea - 1) / 2;
                /*while (_con[fa] < _con[lea])*/
               while (func(_con[fa], _con[lea]))
              //利用仿函数达到比较的效果
             //现在不清楚是大于还是小于了,具体将取决于传入compare的类。
             //通过控制仿函数的类型,达到控制大堆还是小堆的效果。
               {
                       swap(_con[fa], _con[lea]);
                       lea = fa;
                       fa = (lea - 1) / 2;
               }
        }
        void adjustDown(size_t father = 0)
         //向下调整算法,在删除数据后,使空间重新调整为堆时使用
        //默认从根节点开始调整,在建堆时使用就需要传入第一个非叶子节点
        {
               compare func;
               size_t fa = father; //代表父节点
               size_t ch = fa * 2 + 1; //代表子节点
                /*while (ch < _con.size() && _con[fa] < _con[ch])*/
               while (ch < _con.size() && func(_con[fa], _con[ch]))
               {
                        //if (ch + 1 < _con.size() && _con[ch] < _con[ch + 1])//开始默认左子节点大
                       if (ch + 1 < _con.size() && func(_con[ch], _con[ch + 1]))
                              ch++;
                       swap(_con[fa], _con[ch]);
                       fa = ch;
                       ch = fa * 2 + 1;
               }
        }
        //push,pop,top,size,empty部分和上面一样,这里省略
private:
        container _con;
};
test.cpp中
#define _CRT_SECURE_NO_WARNINGS 1
#include"priority_queue.h"
#include<iostream>
#include<vector>
using namespace std;
void test_priority_queue()
{
        myPriority_queue<int,vector<int>, mygreater<int>> pri_qu; //通过传仿函数控制为小堆,默认为大堆。
        
        //剩下的部分一样,这里省略
}
int main()
{
        test_priority_queue();
        return 0;
}
利用模板实现不同比较的调用过程。
当使用默认传参时,compare被myless<T>赋值,compare创建的func对象,是myless的对象,func(_con[fa], _con[lea])会调用myless类中的成员函数,完成比较。当使用mygreater<T>传参时,compare被mygreater<T>赋值,compare创建的func对象,是mygreater的对象,func(_con[fa], _con[lea])会调用mygreater类中的成员函数,完成比较。
下面函数指针的内容了解就可以:
less和greater都是在库中包装好的仿函数,使用需要包头文件<functional>。一般利用仿函数的优势来替换函数指针。如果myPriority_queue传给compare的不是仿函数,而是函数指针会如何?
准备知识:
1.对函数指针解引用,找到对应函数,调用参数,和直接使用函数指针调用参数是一样的,即(*函数指针)(函数参数1.函数参数2)与函数指针(函数参数1,函数参数2)等价,代表函数指针可以直接使用()操作符,这样就能通过函数指针指向对应的比较函数,完成比较。
2.模板会自动推导类型,传什么就是什么,不会构造成单成员变量的类。
先来看一下函数指针的应用:
这是一个函数,*和()相比,()优先级更高,函数名与()结合,为函数。括号内的内容,即函数参数为void (*f)(),f是名称,与*结合,是函数指针,指向一个返回值为void,没有参数的函数。最后剩下:static void(* )()是返回值,即函数的返回值是一个函数指针,指针指向的类型为无参,返回值为static void。
模板具备自动推导的功能,可以兼容使用函数指针完成比较。
bool comIntLess(int x, int y)
{
    return x > y;
}
myPriority_queue<int,vector<int>, bool(* )(int, int)> pri_qu; //模板参数要传类型
按照想象,应该是传入函数指针,compare推导出指针类型,创建func函数指针变量,func(参数1,参数2)调用comIntLess函数。
但是直接这样替换myPriority_queue<int,vector<int>, greater<int>> pri_qu;会出错。
因为compare推导出函数指针类型后,compare func创建的变量是一个没有初始化的函数指针变量,函数指针指向未知。传过去的是类型,没有指向具体函数,不知道调用哪个函数。
要用函数指针,需要在构造的地方传入。同时要兼顾仿函数的传入,这就需要在一些地方做修改:简单来说就是把负责比较的类变成成员变量。
1.添加一个成员变量_comfunc
2.写myPriority_queue的构造函数
myPriority_queue(const compare& x = compare())
    :_comfunc(x)
{}
//传给compare为仿函数类时,x是类,传给compare为函数指针时,x是指针。类创建的_comfunc对象可以调用类的成员函数
//这样是方便传入函数指针,即使不写构造函数,仿函数依然能正常使用,自定义类型为成员变量时会自动调用构造函数,也就是compare()。单凭模板是无法将函数地址传入myPriority_queue中。
//即使这样也不能在传模板参数的时候传入函数指针,而是通过构造函数完成指针的传值。
3.将所有func改为_comfunc
4.在创建优先级队列的地方将函数指针传入:myPriority_queue<int,vector<int>, bool(* )(int, int)> pri_qu(comIntLess)
总之,函数指针也能达到仿函数的效果,但比仿函数复杂,这还只是一个简单函数的情况下。
大多数情况下库中的仿函数就够用,在一些情况下需要自己写。
以c++sort为例:
可以看见,sort中也是有仿函数的。
运行如下程序,及其运行结果:
sort的传参和优先级队列仿函数传参不同的是多了一个(),原因在于优先级队列传的是模板参数,这里传的是对象,优先级队列是在内部创建对象,sort是将创建好的对象传入sort使用。sort是一个函数模板(优先级队列是类模板),传递的是函数的参数,所以这里传的应该是对象,而不是类型。
当需要对自定义类型进行排序的时候,就需要自己写仿函数了,有点类似运算符重载。
sort可以对数组进行排序吗?
数组空间连续,其迭代器本质就是指针,完全可以用sort对数组进行排序。
sort排序的基本原则之一就是要知道要排序对象的大小关系。或者说被排序的对象要有大小的比较规则。
这样的排序是会报错的,因为没有goods类的比较规则。当传入两个goods类的对象到less仿函数中,struct中没有对<的重载,也就无法比较大小,实现升序排序;要进行降序排序也需要对>进行重载。
所以要加上比较方式:
排序前:
排序后:
按照价格升序进行排列。但是也有没解决的问题,如果要按照销量进行排序呢?
<可以被重载两次吗?当然可以,但是要如何解决参数类型不同的问题呢?less中的比较是两个goods对象的比较,即<重载的函数参数就固定为两个goods对象。
这时候仿函数就有用了,就是比较麻烦。当然,这个麻烦是有解决方案的。解决方案需要以后的知识(c++11中)。
将仿函数传给compare作为比较方式。升序的排序方式也是需要仿函数手动实现的,所以说比较麻烦。
重载<和仿函数的区别在于:
重载<函数是被库中用于比较的less仿函数调用的,less在比大小时,调用了operator<;
而手动仿函数则是取代less,完成比较的目的(compare类型为lessPrice,在sort中创建一个lessPrice对象,这个对象来对传入的两个goods对象比大小)。
补充一点:优先级队列有靠区间构造的方式,这个还是很有用的,比如需要对一个数字进行TopK问题的解决,这时用数组的区间构造一个优先级队列就能解决。
在兼容函数指针的版本下继续修改:即新写一个构造函数要支持传迭代器完成建堆
myPriority_queue(InputIterator first, InputIterrator last, const compare& x = compare())
//这里是构造函数,所以没有Container
    :_comfunc(x)
{
    while(first != last)
    {
        _con.push_back(*first);
        first++;
    }
    for(size_t i = (_con.size() - 1 - 1) / 2; i >= 0; i--) //_con.size() - 1是尾节点,(尾节点-1)/2得到第一个非叶子节点
    {
        adjustdown(i);
    }
}

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

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

相关文章

自定义组件中,使用onLoad,onShow生命周期失效问题

的解决方法 自定义组件中&#xff0c;使用onLoad,onShow生命周期失效问题 自定义组件中&#xff0c;使用onLoad,onShow生命周期失效问题 官方文档可查阅到&#xff1a; 页面生命周期仅在page中的vue页面有效&#xff0c;而单独封装的组件中【页面周期无效】&#xff0c;但是Vu…

Pytorch入门(四)使用VGG16网络训练CIFAR10数据集

本文使用PytorchVGG16官方CIFAR10数据集完成图像分类。识别效果如下&#xff1a; 文章目录 一、VGG16 神经网络结构二、VGG16 模型训练三、预测CIFAR10中的是个类别 一、VGG16 神经网络结构 VGG&#xff0c;又叫VGG-16&#xff0c;顾名思义就是有16层&#xff0c;包括13个卷…

地震勘探基础(十)之地震速度关系

地震速度 地震勘探中引入了多种速度的概念&#xff0c;如下图所示。 层速度、平均速度和均方根速度之间的关系 层速度指的是某一套地层垂向上&#xff0c;由于地质条件相对稳定&#xff0c;地层顶底厚度比上地震波的传播时间为层速度&#xff0c;用 v n v_n vn​ 表示。 如下…

一文看懂软件架构4+1视图

目录 一、概述 二、各视图详解 1. 场景视图 2. 逻辑视图 3. 开发视图 4. 处理视图 5. 物理视图 葵花宝典&#xff1a;一看就懂的理解方式 一、概述 41视图包括&#xff1a; 场景视图&#xff08;也叫用例视图&#xff09;&#xff1a;黑盒视图。从外部视角&#xff…

chatgpt赋能python:Python如何分段数据的平均数

Python如何分段数据的平均数 Python是一门极其流行的编程语言&#xff0c;广泛应用于数据分析与科学计算领域。在数据分析中&#xff0c;计算各个数据段的平均数是一项常见的任务。本文将介绍如何使用Python分段计算数据的平均数&#xff0c;以及如何优化这一过程以使速度更快…

Linux中的lrzsz

一、介绍 lrzsz是一款在Linux里可代替ftp上传和下载的程序,也就是一款软件。它是开发者常用的一款工具,这个工具用于windows机器和远端的Linux机器通过XShell传输文件。 二、lrzsz的安装 在安装之前,我们可以使用下述命令先查看yum仓库中是否存在我们要安装的软件: yum…

CentOS7使用Docker快速安装Davinci

环境信息 操作系统&#xff1a;CentOS7Docker : 23.0.6 &#xff08;已配置阿里云镜像加速&#xff09; 安装步骤 安装docker-compose-plugin 官方的例子使用的是docker-compose&#xff0c;但是由于yum能够安装的最新斑斑是1.x,而且官方的docker-compose要求最低版本为2.2以…

首个区块链技术领域国家标准出台 ,中创助力打造区块链技术和应用创新高地

区块链作为数字中国的重要技术底座&#xff0c;正在深刻改变着我国社会生产方式。何谓区块链&#xff0c;对大众来说&#xff0c;也许尚陌生&#xff0c;殊不知&#xff0c;这一产业已稳稳起跑在我国高质量发展的“赛道”上。 近日&#xff0c;《区块链和分布式记账技术参考架…

【JavaScript】超全基础万字大总结

目录 一、初识 JavaScript 1.1 JavaScript 是什么&#xff1f; 1.2 发展历史 1.3 JavaScript 和 HTML 和 CSS 之间的关系 1.4 JavaScript 运行过程 1.5 JavaScript 的组成 二、前置知识 2.1 第一个程序 2.2 JavaScript 的书写形式 2.3 输入输出 三、语法概览 3.1 变…

Linux(CentOS 7) 安装 Mysql8 、Java 以及 mycat2 详细流程

目录 一、Mysql8 安装 1.下载mysql8 2. 解压Mysql 压缩包 3.重名命mysql 文件 4.创建data文件夹 储存文件 5.创建用户组以及用户 6.授权用户 将mysql文件夹的所有者和所有组都改为mysql 7.mysql初始化进入bin目录执行mysqld文件进行初始化 8.编辑my.cnf 9.添加mysqld…

有哪些虚拟化和容器化工具推荐? - 易智编译EaseEditing

以下是几个常用的虚拟化和容器化工具推荐&#xff1a; VMware vSphere&#xff1a; VMware vSphere 是一套完整的虚拟化平台&#xff0c;包括虚拟化服务器、虚拟化存储和虚拟化网络。 它提供了高性能的虚拟机管理和资源调度功能&#xff0c;适用于企业级的虚拟化部署。 Docke…

IT知识百科:什么是跨站脚本(XSS)攻击?

跨站脚本&#xff08;Cross-Site Scripting&#xff0c;XSS&#xff09;是一种常见的网络安全漏洞&#xff0c;攻击者利用该漏洞在受害者的网页中插入恶意脚本&#xff0c;从而能够获取用户的敏感信息、劫持会话或进行其他恶意活动。本文将详细介绍跨站脚本攻击的原理、类型、常…

vue props传值层级多,子级孙子级怎么修改传参

vue props传值层级多了&#xff0c;子级孙子级怎么修改传参 1.出现背景2.怎么在孙组件里改变传过来的值呢2.1这样改是不行的2.2可行的方法2.2.1 引用对象只改变单属性2.2.2 provide和inject 1.出现背景 本来自己写页面的话是直接全部写在一个vue文件里&#xff0c;一个vue文件…

【solidworks】此文档 templates\gba0.drwdot 使用字体长仿宋体,而该字体不可用

一、问题背景 在SolidWorks中绘制工程图纸时&#xff0c;新建一个图纸&#xff0c;打开后就弹出字体错误 此文档 templates\gba0.drwdot 使用字体长仿宋体&#xff0c;而该字体不可用。 二、解决办法 点击选择新的字体&#xff0c;拖到最下面选择汉仪长仿宋体。 上面之所…

41 管理虚拟机可维护性-虚拟机NMI Watchdog

文章目录 41 管理虚拟机可维护性-虚拟机NMI Watchdog41.1 概述41.2 注意事项41.3 操作步骤 41 管理虚拟机可维护性-虚拟机NMI Watchdog 41.1 概述 NMI Watchdog是一种用来检测Linux出现hardlockup&#xff08;硬死锁&#xff09;的机制。通过产生NMI不可屏蔽中断&#xff0c;…

win10+tf2.x+cuda+cudnn踩坑记录( Loaded cuDNN version 8400)

项目场景&#xff1a; 项目用到了tensorflow2.x&#xff1a; 想要用GPU跑算法win10系统下需要安装cuda和cudnn配置带有tenserflow-gpu的环境 问题描述 jyputer运行错误提示&#xff1a;Loaded cuDNN version 8400 Could not locate zlibwapi.dll. Please make sure it is in…

智安网络|保护企业网络空间资产安全的重要性

在数字化时代&#xff0c;企业网络空间资产的安全和保护变得越来越重要&#xff0c;并且拥有安全性能优越、系统完整的企业网络系统&#xff0c;是企业发展的必要条件。但想要实现网络空间安全首先需要关注网络漏洞问题。 保护企业网络空间资产的重要性 网络空间资产安全是企…

【深度学习】跌倒识别(带数据集和源码)从0到1,内含很多数据处理的坑点和技巧,收获满满

文章目录 前言1. 数据集1.1 数据初探1.2 数据处理1.3 训练前验证图片1.4 翻车教训和进阶知识 2. 训练3.效果展示 前言 又要到做跌倒识别了。 主流方案有两种&#xff1a; 1.基于关键点的识别&#xff0c;然后做业务判断&#xff0c;判断跌倒&#xff0c;用openpose可以做到。…

Neural Architecture Search: A Survey

本文是神经架构搜索相关主题的第一篇文章&#xff0c;针对《Neural Architecture Search: A Survey》的一个翻译。 神经架构搜索&#xff1a;综述 摘要1 引言2 搜索空间3 搜索策略4 性能评估策略5 未来方向 摘要 过去几年&#xff0c;深度学习在图像识别、语音识别和机器翻译…

网络故障排除

计算机网络构成了数字业务的基础。为了确保业务连续性&#xff0c;需要日夜监控和管理这些网络背后的 IT 基础架构。IT 管理员在管理 IT 基础架构时经常遇到问题&#xff0c;这是他们工作的关键部分&#xff0c;更重要的部分是解决网络问题。 什么是网络故障排除 网络故障排除…