SGISTL源码探究-大根堆heap

数据结构--堆的实现-大根堆/小根堆/堆排序/堆排序稳定性证明/TOP-K 逆水行舟,不进则退!!! 阅读详情

前言

小根堆和大根堆的概念在数据结构都讲过,等下简单的过一下就行了。在SGISTL的实现中,它并不作为一种容器,而是一系列的算法,用于给priority_queue提供支持,使优先级队列能够体现其优先级。
在SGISTL实现heap中,采用的数据结构并不是使用二叉树实现,而是采用隐式表达,即使用数组表示一个堆。能用数组表示这是因为堆总是一棵完全二叉树(没有节点漏洞):即若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的节点都连续集中在最左边,这就是完全二叉树。

基本概念

小根堆

若根节点存在左孩子,则根节点的值小于左孩子的值;若根节点存在右孩子,则根节点的值小于右孩子的值。即根结点的值为所有结点中的最小值。

大根堆

若根节点存在左孩子,则根节点的值大于左孩子的值;若根节点存在右孩子,则根节点的值大于右孩子的值。即根结点的值为所有结点中的最大值。

插入元素

先将结点插入到堆的尾部,再将该结点逐层向上调整,直到依然构成一个堆,调整方法是看每个子树是否符合大(小)根堆的特点,不符合的话则调整叶子和根的位置。

弹出元素

将根节点弹出后用堆尾结点进行填补,调整二叉树,使之依然成为一个堆。

heap的实现

首先SGISTL中实现的是大根堆,即堆顶节点是元素值最大的节点。使用数组实现一个堆是利用了完全二叉树的性质。如图所示(0号元素不使用):
这里写图片描述
不难发现由于完全二叉树没有空缺的节点,所以第i号元素的左孩子为第2i号元素,而其右孩子为第2i+1号元素,父节点为第i/2号元素。比如1号元素(12)的左孩子为2号元素(9),右孩子为3号元素(10)。
拥有这样的特性,我们就不难实现关于堆以及它的一系列操作了,只需要一个vector以及一些heap操作就能满足我们的要求。不过在SGISTL的具体实现中,0号元素是使用了的。所以第i号元素的左孩子应为第2i+1号元素,而右孩子为第2i+2号元素,父节点为第(i - 1)/2号元素。

这里再提一点,SGISTL的实现heap的各种算法中,有重载了需要元素比较大小标准作为最后一个参数的版本,以下的都是没有元素比较大小标准的版本。

插入元素

提供给priority_queue使用的接口是push_heap,源码如下:

template <class RandomAccessIterator>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last) {
  __push_heap_aux(first, last, distance_type(first), value_type(first));
}

插入元素的时候先将元素插入到末端,然后进行调整。
__push_heap_aux函数的代码如下:

template <class RandomAccessIterator, class Distance, class T>
inline void __push_heap_aux(RandomAccessIterator first,
                            RandomAccessIterator last, Distance*, T*) {
  __push_heap(first, Distance((last - first) - 1), Distance(0),
              T(*(last - 1)));
}

最核心的插入操作是由__push_heap完成的,如下:

template <class RandomAccessIterator, class Distance, class T>
void __push_heap(RandomAccessIterator first, Distance holeIndex,
                 Distance topIndex, T value) {
  //计算得出父节点
  Distance parent = (holeIndex - 1) / 2;
  /* 前面说过堆中插入元素的大致思路
   * 即先将结点插入到堆的尾部
   * 再将该结点逐层向上调整(与父节点交换)
   * 直到依然构成一个符合规则的堆
   */
  while (holeIndex > topIndex && *(first + parent) < value) {
    /* 还未到堆顶并且父节点的值小于插入的值
     * 则将父节点的值下移
     * 将当前节点移动到父节点上
     * 而parent也指向新的父节点
     */
    *(first + holeIndex) = *(first + parent);
    holeIndex = parent;
    parent = (holeIndex - 1) / 2;
  }    
  /* 出循环之后,证明找到了合适的插入位置
   * 进行赋值
   */
  *(first + holeIndex) = value;
}
弹出元素

提供给priority_queue的弹出元素的接口是pop_heap,源码如下:

template <class RandomAccessIterator>
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last) {
  __pop_heap_aux(first, last, value_type(first));
}

而__pop_heap_aux的源码实现如下:

template <class RandomAccessIterator, class T>
inline void __pop_heap_aux(RandomAccessIterator first,
                           RandomAccessIterator last, T*) {
  __pop_heap(first, last - 1, last - 1, T(*(last - 1)), distance_type(first));
}

__pop_heap的源码如下:

template <class RandomAccessIterator, class T, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last,
                       RandomAccessIterator result, T value, Distance*) {
  *result = *first;      //将堆顶元素的值放在堆的末尾
  __adjust_heap(first, Distance(0), Distance(last - first), value); //重新从被取走值的节点开始调整堆(这里的是根结点),使其符合规则,需要新插入的值为原来尾部的值
}

最核心的即__adjust_heap函数,实现如下:

template <class RandomAccessIterator, class Distance, class T>
void __adjust_heap(RandomAccessIterator first, Distance holeIndex,
                   Distance len, T value) {
  //topIndex指向传入的holeIndex结点
  Distance topIndex = holeIndex;
  //holeIndex结点的右孩子的索引
  Distance secondChild = 2 * holeIndex + 2;
  while (secondChild < len) {
    /* 比较左右孩子节点的大小,选择较大的节点作为新的父结点
     * 然后下移
     * 直到移动到该分支的最后一个叶节点为止
     */
    if (*(first + secondChild) < *(first + (secondChild - 1)))
      secondChild--;
    //将除了父节点之外最大的节点的值赋给父结点
    *(first + holeIndex) = *(first + secondChild);
    //下移
    holeIndex = secondChild;
    //找到新的右孩子
    secondChild = 2 * (secondChild + 1);
  }
  /* 如果当前没有右子节点,只有左子节点 */
  if (secondChild == len) {
    //将尾节点的值赋给左子节点的父节点
    *(first + holeIndex) = *(first + (secondChild - 1));
    //下移
    holeIndex = secondChild - 1;
  }
  //调整堆,之所以需要这个操作,是为了弥补在此过程中当value的值同时大于左右两个节点,不满足max-heap这种情况
  __push_heap(first, holeIndex, topIndex, value);
}

执行了pop_back函数后,最大的元素是被置放到了底部容器的最后一个位置,所以可以利用back()等函数取得。

将数据转换成heap

该操作由__make_heap完成

template <class RandomAccessIterator, class T, class Distance>
void __make_heap(RandomAccessIterator first, RandomAccessIterator last, T*,
                 Distance*) {
  //长度小于等于1则无需排列
  if (last - first < 2) return;
  //需要重排的第一个子树的父节点
  Distance len = last - first;
  Distance parent = (len - 2)/2;

  while (true) {
    //调用__adjust_heap,对堆进行调整
    //每次while循环仅确保了first到parent之间的数据满足heap
    __adjust_heap(first, parent, len, T(*(first + parent)));
    //走到了根节点,则结束
    if (parent == 0) return;
    //已经重排了的子树父节点前移
    parent--;
  }
}
对堆进行排序

思路很简单,当我们执行pop_heap操作时,最大堆元素会被放置在容器最后一个元素上,连续多次调用pop_heap,则可以让容器成为一个递增序列了。

template <class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last) {
  while (last - first > 1) pop_heap(first, last--);
}

小结

本小节针对为实现priority_queue而使用的heap进行了分析。如果你对这种数据结构比较熟悉,那应该很容易理解,如果不太熟悉,也没关系,网上有大量的资料详细讲解堆结构并且辅有大量的例子,这里就不详细去介绍它了。
接下来我们就可以看到priority_queue的实现了。

Java~大根堆的创建以及实现堆的插入删除操作 目录 堆的概念以及问题思考 初始化堆并将其调整为大根堆 初始化堆 调整堆为大根堆 大根堆的插入和删除 大根堆的插入 大根堆的删除 完整代码 堆的概念以及问题思考 堆的概念:如果有关键字集合k = {k0,k1,k2,......,k(n-1)},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,且满足Ki <= K2i+1 且Ki <= K2i+2(或Ki >= K2i+1 且Ki >= K2i+2)i = 0,1,2.....,则称为小... 阅读详情

相关推荐

STL应用之大根堆小根堆

定义: priority_queue xxx 大根堆 priority_queue, greater> xxxx 小根堆   访问最值使用 xxx.top() 堆自动把最值维护在堆最上层   删除最值使用 xxx.pop() 删除后自动维护出一个新堆   插入元素使用 xxx.push(xxxx) 插入到元素该在的位置上 并自动维护

求拿1= 2886

大根堆代码说明(堆的构建、插入、删除和堆排序)小根堆同理

我发现网上很多人将的都不清楚,如果只看文章的话,根本就理解不了。所以在我研究完代码之后记录下来,方便次观看,如果有益于跟多人的话那更好了。 写下的都是自己的理解,如果有误欢迎订正。 这里以大根堆举例,适时的会说一下小根堆 讲解代码 目录说在前面两个重要函数siftDown() 和 siftUp()堆的建立插入结点删除结点堆排序全部代码 说在前面 首先,了解一下大根堆or小根堆 (下面简称’堆’)的性质。 堆是一个完全二叉树(最后一层可以不满,上面的每一层都是满的。一个结点若只有一个孩子结点,那一定是.

点滴技术 6047

C++实现大根堆(附完整源码)

C++实现大根堆(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 703

大根堆,小根堆代码

【代码】大根堆,小根堆代码。

weixin_55688516的博客 337

上网第二课:办了百兆宽带,为什么测速只有一半?

最慢的那一环。

qq_36765170的博客 108

ARM的CI-700和Arteris的NoC对比

设计理念:CI-700注重于为移动和客户端SoC提供定制化的互连解决方案,而Arteris的NoC更侧重于提供灵活、可配置的网络互连技术,适用于多种应用场景。性能与功耗:CI-700通过系统级缓存和网状拓扑结构优化性能和功耗,而Arteris的NoC通过物理感知和多协议支持来提升性能和降低功耗。功能安全:Arteris的NoC提供了功能安全支持,这对于汽车和工业应用尤为重要,而CI-700虽然也关注安全性,但未明确提及功能安全支持。互操作性。

ygyglg的专栏 2622

大根堆(大顶堆)代码示例

【代码】大根堆(大顶堆)代码示例。

qq_46463876的博客 478

arcgis密度分析

密度分析 密度分析是指根据输入的要素数据集计算整个区域的数据集状况,从而产生一个连续的密度表面。通过计算密度,将每个采样点的值散布到整个研究区域,并获得输出栅格中每个像元的密度值。在 ArcGIS 中,分布密度的计算方法有点密度分析、线密度分析和核密度分析三种。 1.点密度分析 导入实验数据居民点图层和道路图层 在自定义里勾选空间分析模块Spatial Analyst 点击空间分析工具中密度分析内的点密度分析, 参数设置如下,其中输出像元大小表示输出栅格数据集的像元大小。值越小.

Jasenjane的博客 2万+

C++ STL 中大根堆,小根堆的应用。

1》优先队列:在C++中优先队列默认的是大根堆,如果用小根堆则加入greater. priority_queue<int, vector<int>, less<int>>s;//less表示按照递减(从大到小)的顺序插入元素 priority_queue<int, vector<int>, greater<int>>s;//greater表示按照递增(从小到大)的顺序插入元素 不写第三个参数或者写成less都是大根堆。greater是

兰斯兰斯 2万+

树莓派c语言串口通讯程序

在网上搜了很多关于树莓派串口通讯的例子,但是都是用python写的,虽然python很有名,而且最近也在学习这门语言, 但是还是想用c语言实现一下,因为需要用到整套系统里,其他内容都是用c语言写的。但是网上没有找打现成的。 还好,之前做过天嵌2440开发板的串口程序(参照天嵌工程师提供的代码),发现串口程序其实是通用的, 驱动层屏蔽了硬件细节,个人猜测,几乎所有移植好的系统的串口,都可以用一

realank的专栏 1万+

揭秘cocos-mcp:如何用AI助手提升CocosCreator游戏开发效率

本文深入介绍了cocos-mcp工具,它作为连接AI助手与CocosCreator编辑器的桥梁,遵循MCP协议,能显著提升游戏开发效率。通过自然语言指令,开发者可让AI直接查询场景结构、批量修改属性或生成上下文代码,将重复操作变为智能调度,实现从“手动操作者”到“指令指挥官”的工作流变革。

weixin_29001683的博客 1048

SGISTL源码探究-stl_algo.h中的排序算法

前言在本小节中,我们将分析STL中算法组件的最后一部分:排序算法。排序算法分为内部排序和外部排序。而内部排序中常见的有八种排序算法:直接插入排序、希尔排序、简单选择排序、堆排序、冒泡排序、快速排序、归并排序、桶排序。在SGISTL实现的sort算法中,并不是简单的使用快速排序作为排序算法,而是交叉使用了插入排序、堆排序以及快速排序,并且做了一定的优化,并且只接受迭代器类型为RandomAccessI

Move_now的博客 938

使用Simulink对基于GaN器件的高频电源进行建模与仿真,重点在于优化其效率

本文介绍了基于Simulink的GaN器件高频电源仿真建模方法。通过7个步骤详细讲解系统构建过程:从创建模型、设置电源、配置GaN开关网络,到添加磁性元件、负载和控制器设计,最后进行仿真分析。重点探讨了效率优化策略,包括开关频率选择、磁性元件优化、散热设计改进及控制策略调整。该教程为GaN器件在电力电子系统中的应用提供了完整的仿真流程指导,有助于设计人员验证方案并优化性能。

amy_mhd的博客 232

数据结构—堆 大根堆 小根堆 STL实现

我是真的不会 优先队列创建小根堆的遍历,所以我就通过调试的方法,给大家展示了。

up_up_up_的博客 1634

历年安徽省省赛AHOI初中组考点分类汇总考点分类汇总

持续更新中.....

weixin_42358969的博客 818

uniapp 如何封装实现任意页面都能使用的全局弹窗

项目内容场景全局监听消息提示难点uniapp 无法全局挂载组件解决方案动态创建 DOM + 动态 Vue 实例优势跨页面 / 不依赖页面组件树 / 灵活调用如果你使用的是小程序平台,DOM 操作可能受限,需要用或方式兼容处理。想支持多个弹窗堆叠?你可以扩展,加个弹窗队列管理器。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 944

python实现大根堆

堆是一棵完全二叉树,即除了最后一层外,其它各层的节点数都达到最大个数,且最后一层的所有节点都连续集中在最左边。

m0_67713667的博客 1119

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

C++ STL 用法解释

C++ STL

是芜湖的,更是世界的! 2692

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

数据结构学习——堆(含大根堆代码)

大根堆, 堆的知识要点和代码

YiHeboy的博客 546

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

数据结构与算法_排序算法_二叉堆(大根堆小根堆)算法和接口实现

二叉堆定义 大根堆和小根堆定义 二叉堆代码实现

weixin_43916755的博客 1360

堆——神奇的优先队列 大根堆小根堆详解,附小根堆C++代码实现与STL相关

文章目录前置知识堆操作小根堆插入代码实现小根堆删除代码实现测试代码STL实现——priority_queue1.使用2.创建3.成员函数 前置知识 堆是一个完全二叉树(最后一层可以不满,上面的每一层都是满的。一个结点若只有一个孩子结点,那一定是它的左孩子。如下图)这是一个逻辑上基于完全二叉树、物理上一般基于线性数据结构(如数组、向量、链表等)的一种数据结构。 完全二叉树最重要的性质:如果n个节点的完全二叉树的节点按照层次并按从左到右的顺序从0开始编号,对于人一个绩点都有: 序号为0的节点是根 对

哇咔咔的博客 8950

C++STL priority_queue 学习

<br /><br />毋意,毋必,毋固,毋我。 言必行,行必果。C++STL priority_queue 学习<br />本文来源自网上摘抄,故学习记录于此。<br /> <br />包含priority_queue 的头文件是 <queue><br /> <br />priority_queue类的主要成员:<br />priority_queue();    //默认构造函数,生成一个空的排序队列<br />priority_queue(const queue&);    //拷贝构造函数<br /

SG的专栏 1万+

C语言经典算法之建立大根堆

在C语言中,建立大根堆(Max Heap)的过程通常涉及到对一个数组进行操作。大根堆是一个完全二叉树,其中每个节点的值都不小于其子节点的值。

weixin_56154577的博客 1313

堆排序算法代码(大根堆)

算法Sift将根节点与左右子树的根节点进行比较,若不满足堆的条件,则将根节点与左右子树根节点中的较大者 进行交换。所以,每进行一次比较,需要调整完全二叉树的问题规模就减少一半。因此,其时间性能是O(log^2 n)

TFTNT080625的博客 301

STL源码——heap算法

一、heap概述 heap并不归属于 STL 容器组件 binary heap作为priority queue的底层实现机制。顾名思义,priority queue允许使用者以任何次序将任何元素推入容器内,但取出时一定是从优先权最高(也就是数值最高)之元素开始取。binary max heap正是具有这样的特性,适合做为priorityqueue的底层机制 heap作为priority queue的底层实现 假设list作为priority queue的底层机制: 元素插入动作可享常数时间 但是要找到l

一蓑烟雨任平生 1482

一文告别堆:堆的结构代码+堆排序

堆的概述加代码实现 大根堆和小根堆 普通数组堆化的代码(两种方法) public static void heapInsert(int[] heap,int index){//先插入到数组的最后一个位置,然后根据那个位置去调整堆[大根] while(heap[index]>heap[(index-1)/2]){//一直和父亲比较(到了根节点,还没有结束,根节点的父亲还是根节点) swap(heap,index,(index-1)/2);

m0_56740080的博客 224
上一篇: SGISTL源码探究-queue配接器
下一篇: SGISTL源码探究-优先级队列
UKey_
博客等级 码龄10年 258粉丝 186原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值