【C++STL详解(十)】--------priority_queue的模拟实现

news2024/11/19 23:36:10

目录

前言

一、堆的向上调整算法

二、堆的向下调整算法

三、优先队列模拟实现

Ⅰ、接口总览

Ⅱ、各个接口实现

1.构造函数

2.仿函数

3.向上调整

4.向下调整

5.其余接口

Ⅲ、完成代码


前言

上节内容我们简单的介绍了关于priority_queue的使用内容,我们明白了它的默认容器是vector,以及优先队列实际上默认就是个大堆等相关知识,那么接下来就来看看底层的模拟实现究竟是什么样的!但在此之前先简单介绍两个堆算法,向上调整和向下调整算法!

一、堆的向上调整算法

我们在数据结构中都知道堆在物理空间上是采用数组去存储的,但是呢在逻辑上我们可以将其看作一棵完全二叉树,形如:

以上这个是大堆,小堆反之,下面以大堆为例介绍向上调整算法!

向上调整:

①在大堆的末尾插入一个数据,然后和其父亲结点去比较!

②如果大于父亲结点,那就和父亲结点交换位置,并更新父亲结点,直到比父亲结点小;如果比父亲结点小,那就停止交换!此时就是大堆了!

小堆过程相反!!把小的向上调即可

注意:在数据结构的树与二叉树中提到过父亲结点和孩子结点的下标关系

左孩子=父亲*2+1;

右孩子=父亲*2+2;

例如,在上述堆中插入一个数据77。过程如下:

和其父亲结点比较发现,比父亲结点大,那就交换!

在去新的父节点比较,即和66相比,比它大,交换!

到这里就调整完毕了,此时就是个大堆了!

具体代码如下:

//建大堆
void AdjustUp(vector<int>& v1 int child)
{
	int parent = (child - 1) / 2;//通过父子下标关系得出

	while (child > 0)
	{
		if (v[child] > v[parent])
		{
			swap(v[child], v[parent]);//交换

			child = parent;//更新孩子
			parent = (child - 1) / 2;//更新父亲
		}

        //至此已成堆
		else
		{
			break;
		}
	}
}

二、堆的向下调整算法

同样还是以大堆为例,进行向下调整,但是这里有个前提:一定要保证左右子树是一个大堆,才可以进行向下调整!建小堆,也是要保证左右子树都是小堆才可以!

向下调整:

①从堆顶向下,先选出当前父节点的左右孩子中的最大节点,然后再用当前节点去和最大节点的比较!

②如果父节点小于最大孩子节点,那就交换父子节点,并重新更新父子节点;如果大于最大节点,那就不能交换,此时就是大堆了!

小堆就是相反的,实际就是把大的向下调!大堆就是把小的向下调!!

例如,上图先找出左右孩子中的最大节点,即77作为最大孩子。22与77相比,22比77小,那就交换!

再重复上述步骤,因为只有33这个节点,并且22小于33,即父亲小于孩子,那就交换!

至此已经来到了末尾,交换结束,此时的结构就是大堆!!!!

具体实现代码如下:

void Adjustdown(vector<int>& a, int size, int parent)
{
	int child = parent * 2 + 1;//左孩子
	while (child<size)
	{
		//找左右孩子哪个大,把大给child
		if (child + 1 < size && a[child + 1] > a[child])
		{
			child = child + 1;
		}

        //比较孩子和父亲
		if (a[child] > a[parent])
		{
			swap(a[child], a[parent]);
			parent=child;//更新父亲
			child = parent * 2 + 1;//更新孩子
		}

        //至此已成大堆
		else
		{
			break;
		}
	}
}

总结一下:向上调整就是拿孩子去比父亲,所以参数得是孩子的;向下调整实际就是拿父亲去比孩子,所以参数得父亲的下标!!

注意:实际应用中,大多数都是采用向下调整建堆,因为时间复杂度为O(N),而向上调整时间复杂度为O(N*logN);

三、优先队列模拟实现

有上面两个算法的铺垫,接下来的模拟实现就简单很多了!

Ⅰ、接口总览

#include<vector>

namespace Pq
{
    //仿函数,控制比较方式
    template <class T>
    class less
    {
    public:
        bool operator()(const T& x, const T& y);
    };

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


    template <class T, class Container = vector<T>, class Compare = less<T> >
    class priority_queue
    {
    public:
        //构造空队列
        priority_queue();

        //迭代器区间构造队列
        template <class InputIterator>
        priority_queue(InputIterator first, InputIterator last);

        void push(const T& x);
        void pop();

        bool empty() const;
        size_t size() const;
        const T& top() const;
    private:
        Container c;
        Compare comp;

        //向上调整算法
        void AdjustUp(size_t child);
        //向下调整算法
        void Adjustdown(size_t parent);
    };

};

注意:一样的,模拟实现,毕竟只是模拟,一定要记得在自己的空间里面去模拟哦!同时我们这里为了更真实的去模拟,我们将向上调整和向下调整设置为私有函数!!!因为平时去调用时,根本就看不见这两个函数,是吧哥们!

Ⅱ、各个接口实现

1.构造函数

  • 构造空队列
//构造空队列
priority_queue()
    :c()
{}
  • 迭代器区间初始化

写法一:

//迭代器区间构造队列
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);
       }
}

这样的写法实际和vector、list等模拟实现相类似,都是通过尾插操作去实现的!但是要注意一点,优先队列就是个堆结构,插入数据时应该要调整它的结构,前面也说过向下调整时间复杂度低,所以这里采用向下调整算法建堆,但是一定要注意向下调整是有前提的必须要求左右子树都是一个大堆(或者小堆),因此我们应该从最后一个非叶子结点开始去调整,也就是最后一个父结点开始向下调整!!!!

写法二:

template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
    :c(first,last)
{

     for (int i = (c.size() - 1 - 1) / 2; i >= 0; i--)
      {
            Adjustdown(i);
      }
}

这个写法就是利用优先队列实际上是一个容器适配器,也就是说用别人的东西去创造自己,也就是它的成员变量实际上就是对应容器,相当与一个自定义类型,那么对于自定义类型,他就会去调用自己的构造函数完成初始化工作!

例如:当传进来的是vector容器时,优先队列里面的成员变量就是vector示例化出来的对象,对这个对象进行初始化工作,实际上就是在调用vector的默认成员函数完成构造!!!

2.仿函数

这里在前面的优先队列介绍中就有涉及,要注意一点仿函数可以控制比较逻辑,在优先队列的底层,大堆(less)实际上是用<比较,小堆(greater)实际上是用>比较!

//大堆,<比较
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;
    }
};

3.向上调整

//向上调整算法(大堆为例)
void AdjustUp(size_t child)
{
     size_t parent = (child - 1) / 2;
     while (child > 0)
     {
         //if (_con[parent] < _con[child])
          if (comp(c[parent], c[child]))
          {
                 swap(c[parent], c[child]);
                 child = parent;
                 parent = (child - 1) / 2;
           }

           else
           {
               break;
           }
     }
}

注意:整体逻辑和上面讲到的差不多,只不过这里的比较逻辑采用了仿函数,comp实际上是仿函数实例化出来的对象,在成员变量里面了!

4.向下调整

 //向下调整算法(默认大堆)
 void Adjustdown(size_t parent)
 {
     size_t child = 2 * parent + 1;
     while (child < c.size())
     {
         if (child + 1 < c.size() && comp(c[child], c[child + 1]))//仿函数控制比较逻辑
         {
             child = child + 1;
         }

         //用仿函数                
         if (comp(c[parent], c[child]))
         {
             swap(c[parent], c[child]);
             parent = child;
             child = 2 * parent + 1;
         }

         //至此已成大堆
         else
         {
             break;
         }
     }
 }

5.其余接口

void push(const T& x)
{
     c.push_back(x);
     AdjustUp(c.size() - 1);//最后一个元素向上调整
}

void pop()
{
     swap(c[0], c[c.size() - 1]);
     c.pop_back();

     //在使用向下调整堆结构
     Adjustdown(0);
}

bool empty() const
{
    return c.empty();
}

size_t size() const
{
    return c.size();
}

const T& top() const
{
    return c[0];
}

需要注意的是堆的删除操作(pop),它实际上就是先把堆顶元素与最后一个元素交换,然后在把最后一个元素不看做堆的元素,也就是删除,最后在采用向下调整堆结构!!

例如:

Ⅲ、完成代码

#pragma once
#include<vector>

namespace Pq
{
    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 Container = vector<T>, class Compare = less<T> >
    class priority_queue
    {
    public:
        //构造空队列
        priority_queue()
            :c()
        {

        }

        //迭代器区间构造队列
        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);
            }
        }

        void push(const T& x)
        {
            c.push_back(x);
            AdjustUp(c.size() - 1);//最后一个元素向上调整
        }

        void pop()
        {
            swap(c[0], c[c.size() - 1]);
            c.pop_back();
            Adjustdown(0);
        }

        bool empty() const
        {
            return c.empty();
        }
        size_t size() const
        {
            return c.size();
        }
        const T& top() const
        {
            return c[0];
        }
    private:
        Container c;
        Compare comp;

        void AdjustUp(size_t child)
        {
            size_t parent = (child - 1) / 2;
            while (child > 0)
            {
                //if (_con[parent] < _con[child])
                if (comp(c[parent], c[child]))
                {
                    swap(c[parent], c[child]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else
                {
                    break;
                }
            }
        }
        void Adjustdown(size_t parent)
        {
            size_t child = 2 * parent + 1;
            while (child < c.size())
            {
                if (child + 1 < c.size() && comp(c[child], c[child + 1]))
                {
                    child = child + 1;
                }          
                if (comp(c[parent], c[child]))
                {
                    swap(c[parent], c[child]);
                    parent = child;
                    child = 2 * parent + 1;
                }
                else
                {
                    break;
                }
            }
        }
    };

};

今天就分享到这里,如果对你有帮助,请多多支持,你的支持是我更新的动力!!

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

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

相关文章

鸿蒙OpenHarmony开发板解析:【系统能力配置规则】

如何按需配置部件的系统能力 SysCap&#xff08;SystemCapability&#xff0c;系统能力&#xff09;是部件向开发者提供的接口的集合。 开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 部件配置系统…

17 【Aseprite 作图】参考图和颜色

参考图 Aseprite 作图&#xff0c;“打开 - 一张参考图”&#xff0c;再把参考图拉到右边&#xff0c;就可以得到参考图和缩略图 取消选区 通过“选择 - 取消选择”&#xff0c;可以 取消选区 复制参考图的颜色 打开参考图后&#xff0c;参考图的调色板就会出现参考图所有的…

视频号小店保证金,服务费,手续费是多少?货款结算周期多长?

大家好&#xff0c;我是电商糖果 随着视频号小店越来越火&#xff0c;很多商家都想入驻小店。 入驻之前大家对视频号的收费问题都比较好奇。 糖果2022年就开始做店的了&#xff0c;对小店的保证金&#xff0c;服务费的&#xff0c;手续费&#xff0c;货款结算周期都非常了解…

Windows11 同时安装jdk8和jdk17 可切换

Windows11 同时安装jdk8和jdk17 可切换 死忠于JDK8的码农们&#xff0c;可能不得不做出一些改变的 因为在springboot3最低也是只能用17 并且最近如果创建springboot项目的时候&#xff0c;你会发现&#xff0c;最低也是17的 并且&#xff0c;如果使用springcloud开发&#x…

axios异步操作第一篇

1 同步请求和异步请求 客户端给java后台程序发送请求&#xff0c;发送请求的方式有两种&#xff1a; 同步请求 同步请求发送方式&#xff1a; 1 浏览器地址栏&#xff0c;输入url&#xff1a;http://localhost:8080/web-app/xxxServlet 2 3 删除 4 javascript:location.hr…

CrossManager软件安装

目录 一、CrossManager软件 1.1 下载安装程序&#xff1a; 1.2 注册-登录 1.3 运行安装程序 1.4 完成安装&#xff1a; 1.5 激活软件&#xff1a; 文章底部可获取安装包---CrossManager软件安装&#xff08;有效期30天&#xff09; 当涉及到专业的软件安装和配置时&…

脑机接口(BCI)助力失语者重获交流能力:纽约大学最新研究突破

近年来&#xff0c;脑机接口&#xff08;BCI&#xff09;技术取得了显著的进展&#xff0c;引发了科研界和公众的广泛关注。特别是在失语症的治疗领域&#xff0c;BCI技术为那些因神经系统缺陷而失去交流能力的患者带来了新的希望。失语症不仅严重影响了患者的日常生活&#xf…

通俗的理解网关的概念的用途(四):什么是网关设备?(网络层面)

任何一台Windows XP操作系统之后的个人电脑、Linux操作系统电脑都可以简单的设置&#xff0c;就可以成为一台具备“网关”性质的设备&#xff0c;因为它们都直接内置了其中的实现程序。MacOS有没有就不知道&#xff0c;因为没用过。 简单的理解&#xff0c;就是运行了具备第二…

给网络镜像模式下的 WSL2 使用 127.0.0.1代理的方法

网络镜像模式下的WSL2虽然复制了宿主机windows的ip&#xff0c;但是仍然无法访问127.0.0.1的代理。经过调查&#xff0c;发现因为WSL2从应用商店下载而来&#xff0c;所以可能是UWP应用&#xff0c;所以需要用工具解除环回代理限制。

数据中心法

数据中心法是实现词法分析器的结构化方法。通过设计主表和子表分开存储状态转移信息&#xff0c;实现词法分析器的控制逻辑和数据结构分离。 主要解决了状态爆炸、难以维护和复杂性的问题。 状态爆炸是指当状态和转移较多时&#xff0c;单一使用一个表来存储所有的信息的话会导…

韩顺平0基础学Java——第8天

p155-168 数组&#xff08;第六章&#xff09; 数组可以存放多个同一类型的数据&#xff0c;数组也是一种数据类型&#xff08;引用类型&#xff09;。 即&#xff0c;数组就是一组数据~ 例&#xff1a;double [] hens {1,2,3,4,5,6}; 新建了一组鸡&#xff0c;里面有6个。…

画出入学管理系统的顶层图和1层图

&#xff08;学校作业&#xff09; 题目如下&#xff1a; 某培训机构入学管理系统有报名、交费和就读等多项功能&#xff0c;下面是对其各项功能的说明&#xff1a; 1、报名&#xff1a;由报名处负责&#xff0c;需要在学员登记表上进行报名登记&#xff0c;需要查询课…

教你解决PUBG绝地求生卡在初始界面 登不上去 打不开游戏的问题

在热门大逃杀游戏《绝地求生》&#xff08;PUBG&#xff09;里&#xff0c;紧张刺激的战斗和高度还原的战场环境深深吸引着全球玩家的心。然而&#xff0c;在经历一场紧张激烈的生存挑战后&#xff0c;部分玩家遭遇了一段不太愉快的小插曲&#xff1a;游戏在胜利或战败的结算界…

视频批量剪辑指南:一键合并视频并添加背景音乐,高效便捷

在数字化时代&#xff0c;视频剪辑已经成为了一项常见且重要的技能。无论是制作家庭影片、工作展示还是社交媒体内容&#xff0c;掌握高效的视频剪辑技巧都能极大地提升我们的工作效率和创作质量。本文将为您介绍云炫AI智剪中高效的视频批量剪辑方法&#xff0c;让您能够一键合…

【LLM 论文】Step-Back Prompting:先解决更高层次的问题来提高 LLM 推理能力

论文&#xff1a;Take a Step Back: Evoking Reasoning via Abstraction in Large Language Models ⭐⭐⭐⭐ Google DeepMind, ICLR 2024, arXiv:2310.06117 论文速读 该论文受到的启发是&#xff1a;人类再解决一个包含很多细节的具体问题时&#xff0c;先站在更高的层次上解…

vue----- watch监听$attrs 的注意事项

目录 前言 原因分析 解决方案 总结 前言 在 Vue 开发过程中&#xff0c;如遇到祖先组件需要传值到孙子组件时&#xff0c;需要在儿子组件接收 props &#xff0c;然后再传递给孙子组件&#xff0c;通过使用 v-bind"$attrs" 则会带来极大的便利&#xff0c;但同时…

分解质因数-第12届蓝桥杯国赛Python真题解析

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第61讲。 分解质因数&#…

数据库备份与恢复--06---MySQL集群高可用架构之MHA

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 MySQL集群高可用架构之MHA1.什么是MHAMHA&#xff08;MasterHigh Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件 &#xff0c;m…

基于springboot实现疾病防控综合系统项目【项目源码+论文说明】

基于springboot实现疾病防控综合系统演示 摘要 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何一个企业或者个人会忽视&#xff0c;如何让信息急速传递&#xff0c;并且归档储存查询&#xff0c;采用之前的纸张记录模式已经不符合当前使用要求了。所以&…

HCIP的学习(OSPF总篇)

HCIA的复习 这边可以与我之前写的HCIA博客结合起来一起看&#xff0c;效果更好 HCIA的学习&#xff08;6&#xff09; OSPF状态机 down—关闭-----一旦启动OSPF进程&#xff0c;并发出hello报文&#xff0c;则进入下一个状态init----初始化状态------当收到的hello报文中存在…