STL —heap算法源码刨析 make_heap、push_heap、pop_heap、sort_heap操作分析
STL —heap算法源码刨析
- heap算法概述
- push_heap 插入元素
- pop_heap 取出根节点元素
- sort_heap 按极值存放元素
- make_heap 将一段现有数据构造成heap
- 程序测试
heap算法概述
heap的内部是一个完全二叉树,将极值存放在根节点。这个里的极值可分为最大值、最小值。根据极值意义的不同可分为大堆和小堆。为了方便解释,以下的讲解内容都以大堆(max-heap)的例子讲解。
大顶堆:
根节点(堆顶元素)是所有节点中的最大值(父节点都大于左右子节点)。
小顶堆:
小顶堆中的根节点是所有节点中的最小值(父节点都小于左右子节点)。
如上图所示,A即为这个列表中的极值。在查找极值时极为方便。同时因为是一个完全二叉树,所以除了叶子节点外,其他的节点都存在。因此可以通过索引值,来找到父节点和左右子节点之间的关系。当父节点索引值为i时,左子节点索引值2i+1,右子节点索引值为2i+2。
具体原理可查看此博客:堆排序中 i 位置的节点的子节点位置为 2i+1, 2i+2, 父节点为 (i-1) / 2
push_heap 插入元素
数据插入示意图
将要插入的元素,放入容器的尾端。通过调用push_heap()函数,将尾端的元素放入到完全二叉树的合适位置。
//计算元素之间的距离
template<class ForwardIterator>
typename std::iterator_traits<ForwardIterator>::difference_type Distance(ForwardIterator first, ForwardIterator last)
{typename std::iterator_traits<ForwardIterator>::difference_type n = 0;while (first != last){++first;++n;}return n;
}//获取元素类型
template<class Iterator>
inline typename std::iterator_traits<Iterator>::value_type* value_Type(const Iterator&)
{return static_cast<std::iterator_traits<Iterator>::value_type *> (0);
}//获取表示距离的类型
template<class Iterator>
inline typename std::iterator_traits<Iterator>::difference_type* distance_type(const Iterator&)
{return static_cast<std::iterator_traits<Iterator>::difference_type *> (0);
}
template<class RandomAcessIterator, class Distance, class T>
void _push_heap(RandomAcessIterator first, Distance holeIndex, Distance topIndex, T value)
{Distance parent = (holeIndex - 1) / 2; //找到父节点while (holeIndex > topIndex && *(first + parent) < value) //当索引值不为根节点时,或者父节点小于插入空节点时{*(first + holeIndex) = *(first + parent);//父节点的值复制给空节点holeIndex = parent; //父节点索引值复制给空节点parent = (holeIndex-1) / 2; //查找此时空节点的父节点}*(first + holeIndex) = value; //将值复制到此时的空节点上
}template<class RandomAcessIterator, class Distance, class T>
inline void _push_heap_aux(RandomAcessIterator first, RandomAcessIterator last, Distance*, T*)
{int count = last - first;_push_heap(first, Distance((last - first) - 1), Distance(0), T(*(last - 1)));
}template <class RandomAcessIterator>
inline void USD_push_heap(RandomAcessIterator first, RandomAcessIterator last)
{_push_heap_aux(first, last, distance_type(first), value_Type(first));
}
这是是一个插入操作,将数据写入到字符串的尾端。找到尾端所对应的父节点,进行对比。如果大于父节点,两个节点对调。找到根节点或者插入空节点小于父节点时,循环操作结束。此时插入的值被放入合适的位置。元素的插入,可参考示意图的操作。
pop_heap 取出根节点元素
调用 函数pop_heap(),会将根节点的值取出来,存在放在容器的尾端。同时会按照完全二叉树的规则重新移动剩余的元素,找出剩余元素中的极值,存放在根节点中,构建出一个新的完全二叉树。此时要取出的元素位于尾部,再通过容器自身的操作,取出尾部的元素。
元素取出示意图
template<class RandomAcessIterator, class Distance, class T>
void _adjust_heap(RandomAcessIterator first, Distance holeIndex, Distance len, T value)
{Distance topIndex = holeIndex; //根节点Distance sencondChild = 2 * holeIndex + 2; //右子节点while (sencondChild < len){if (*(first + sencondChild) < *(first + sencondChild - 1)) //右子节点与左子节点进行比较{sencondChild--; //通过移动,找到左子节点}*(first + holeIndex) = *(first + sencondChild);//将较大的值复制给空值元素所在的位置holeIndex = sencondChild; //空值元素移动到较大值元素位置上sencondChild = 2 * sencondChild + 2; //再次查找对应的右子节点}if (sencondChild == len) //此时右子节点等于树的长度,那么右子树不存在,将左子树元素复制给空值元素{*(first + holeIndex) = *(first + sencondChild-1);holeIndex = sencondChild-1;}_push_heap(first, holeIndex, topIndex,value);//将原二叉树的尾部元素,插入到现在空值元素位置上
}template<class RandomAcessIterator,class T,class Distance>
inline void _pop_heap(RandomAcessIterator first, RandomAcessIterator last, RandomAcessIterator result,T value,Distance *)
{*result = *first;_adjust_heap(first, Distance(0), Distance(last - first), value);
}template<class RandomAcessIterator,class T>
inline void _pop_heap_aux(RandomAcessIterator first, RandomAcessIterator last,T*)
{_pop_heap(first, last-1, last - 1, T(*(last - 1)), distance_type(first));
}template<class RandomAcessIterator>
inline void USD_pop_heap(RandomAcessIterator first, RandomAcessIterator last)
{_pop_heap_aux(first,last,value_Type(first));
}
此算法中的核心逻辑调用_adjust_heap(),首先holeIndex = 0,此时空值元素代表的是根节点,找到根节点对应的右子节点,根节点的右子节点和左子节点进行相比。空值元素与相比后较大值元素进行对换,此时较大值元素就作为了根节点,同时空值元素,移动到了较大值的位置。重复上述操作在继续查找控制元素的右子节点,与它的左子节点进行对比。只到所查值的右子节点索引值大于树的长长度。则停止查找。查找结束后,将原完全二叉树的尾节点,复制到空值所在的位置。操作流程,可参考此函数的示意图。
sort_heap 按极值存放元素
将元素按照极值,从尾到前开始存放元素
template<class RandomAcessIterator>
void USD_sort_heap(RandomAcessIterator first, RandomAcessIterator last)
{while (last - first>1){USD_pop_heap(first,last--);}
}
通过使用pop_heap()操作,将结果存放在尾部,同时缩短last的索引值。这样每pop_heap一次,就会构造一个新的二叉树,同时尾部会存放上一个二叉树的根节点。
make_heap 将一段现有数据构造成heap
容器中存放一段数据,通过make操作后,会更改容器中元素的位置。从而满足 堆的要求。
template<class RandomAcessIterator,class T,class Distance>
void _make_heap(RandomAcessIterator first, RandomAcessIterator last, T*, Distance*)
{if (last - first<2){return;}Distance len = last - first;Distance parent = (len - 2) / 2; //最后一个父节点while (true){_adjust_heap(first, parent, len, *(first+ parent));if (parent == 0){return;}parent--;}
}template<class RandomAcessIterator>
inline void USD_make_heap(RandomAcessIterator first, RandomAcessIterator last)
{_make_heap( first,last, value_Type(first), distance_type(first));
}
此算法的思路,首先找到最后一个父节点。通过调用_adjust_heap()函数,来调整此时的父节点和左右两个子节点之间的关系。来满足heap的条件。当此节点和其子节点调整结束后,通过移动parent索引值,找到下一个父节点再重复上一个操作。当查找到根节点即索引值为0时,heap构建完成。
程序测试
//heap.h
#pragma once
#include <algorithm>template<class ForwardIterator>
typename std::iterator_traits<ForwardIterator>::difference_type Distance(ForwardIterator first, ForwardIterator last)
{typename std::iterator_traits<ForwardIterator>::difference_type n = 0;while (first != last){++first;++n;}return n;
}template<class Iterator>
inline typename std::iterator_traits<Iterator>::value_type* value_Type(const Iterator&)
{return static_cast<std::iterator_traits<Iterator>::value_type *> (0);
}template<class Iterator>
inline typename std::iterator_traits<Iterator>::difference_type* distance_type(const Iterator&)
{return static_cast<std::iterator_traits<Iterator>::difference_type *> (0);
}template<class RandomAcessIterator, class Distance, class T>
void _push_heap(RandomAcessIterator first, Distance holeIndex, Distance topIndex, T value)
{Distance parent = (holeIndex - 1) / 2;while (holeIndex > topIndex && *(first + parent) < value){*(first + holeIndex) = *(first + parent);holeIndex = parent;parent = (holeIndex-1) / 2;}*(first + holeIndex) = value;
}template<class RandomAcessIterator, class Distance, class T>
inline void _push_heap_aux(RandomAcessIterator first, RandomAcessIterator last, Distance*, T*)
{int count = last - first;_push_heap(first, Distance((last - first) - 1), Distance(0), T(*(last - 1)));
}template <class RandomAcessIterator>
inline void USD_push_heap(RandomAcessIterator first, RandomAcessIterator last)
{_push_heap_aux(first, last, distance_type(first), value_Type(first));
}template<class RandomAcessIterator, class Distance, class T>
void _adjust_heap(RandomAcessIterator first, Distance holeIndex, Distance len, T value)
{Distance topIndex = holeIndex;Distance sencondChild = 2 * holeIndex + 2;while (sencondChild < len){if (*(first + sencondChild) < *(first + sencondChild - 1)){sencondChild--;}*(first + holeIndex) = *(first + sencondChild);holeIndex = sencondChild;sencondChild = 2 * sencondChild + 2;}if (sencondChild == len){*(first + holeIndex) = *(first + sencondChild-1);holeIndex = sencondChild-1;}_push_heap(first, holeIndex, topIndex,value);
}template<class RandomAcessIterator,class T,class Distance>
inline void _pop_heap(RandomAcessIterator first, RandomAcessIterator last, RandomAcessIterator result,T value,Distance *)
{*result = *first;_adjust_heap(first, Distance(0), Distance(last - first), value);
}template<class RandomAcessIterator,class T>
inline void _pop_heap_aux(RandomAcessIterator first, RandomAcessIterator last,T*)
{_pop_heap(first, last-1, last - 1, T(*(last - 1)), distance_type(first));
}template<class RandomAcessIterator>
inline void USD_pop_heap(RandomAcessIterator first, RandomAcessIterator last)
{_pop_heap_aux(first,last,value_Type(first));
}template<class RandomAcessIterator>
void USD_sort_heap(RandomAcessIterator first, RandomAcessIterator last)
{while (last - first>1){USD_pop_heap(first,last--);}
}template<class RandomAcessIterator,class T,class Distance>
void _make_heap(RandomAcessIterator first, RandomAcessIterator last, T*, Distance*)
{if (last - first<2){return;}Distance len = last - first;Distance parent = (len - 2) / 2; //最后一个父节点while (true){_adjust_heap(first, parent, len, *(first+ parent));if (parent == 0){return;}parent--;}
}template<class RandomAcessIterator>
inline void USD_make_heap(RandomAcessIterator first, RandomAcessIterator last)
{_make_heap( first,last, value_Type(first), distance_type(first));
}
#include "heap.h"
#include <vector>int main()
{int a[5] = {1,2,3,4,5};std::vector<int> vecTest(a,a+5);USD_make_heap(vecTest.begin(), vecTest.end()); //5,4,3,1,2 vecTest.push_back(7);USD_push_heap(vecTest.begin(), vecTest.end()); //7,4,5,1,2,3USD_pop_heap(vecTest.begin(), vecTest.end());//5,4,3,1,2,7vecTest.pop_back();//5,4,3,1,2USD_sort_heap(vecTest.begin(), vecTest.end());//1,2,3,4,5return 0;
}
为了方便,上述实现都采用了“<”作为比较操作符。这块比较操作,可封装成仿函数,来满足用户自定义的比较操作。注意:写仿函数时,注意返回的bool值。从而决定构建的是max-heap或者min-heap。
如果想了解,算法封装仿函数操作可参考:STL算法详细解剖——单纯数据处理函数
此文章,列举了相同算法,封装仿函数与不封装仿函数两种形式。未了解过仿函数的读者,可做初步的了解。
相关文章:

STL —heap算法源码刨析 make_heap、push_heap、pop_heap、sort_heap操作分析
STL —heap算法源码刨析 heap算法概述push_heap 插入元素pop_heap 取出根节点元素sort_heap 按极值存放元素make_heap 将一段现有数据构造成heap程序测试 heap算法概述 heap的内部是一个完全二叉树,将极值存放在根节点。这个里的极值可分为最大值、最小值。根据极值…...

走进低代码表单开发(一):可视化表单数据源设计
在前文,我们已对勤研低代码平台的报表功能做了详细介绍。接下来,让我们深入探究低代码开发中最为常用的表单设计功能。一个完整的应用是由众多表单组合而成的,所以高效的表单设计在开发过程中起着至关重要的作用。让我们一同了解勤研低代码开…...

简单好用的OCR API
现如今,越来越多的科技产品可以帮助我们改善和提高相应的工作效率。OCR技术的出现,提高了人们的工作效率,其应用领域及其广泛。就拿应用了OCR技术的翔云文档识别服务来说,只需上传文档图片便可自动识别并返回文档中相应的内容。翔…...
c++的拷贝构造函数和赋值函数
拷贝构造函数和赋值函数 什么是拷贝构造 是一种特殊构造函数,如果没有显式的实现,编译器就会自动生成。 class 类名 { public:// 拷贝构造类名(const 类名& that){} }; 什么时候会调用拷贝构造 当使用一个类对象给另一个新的类对象初始化时&…...

什么自动猫砂盆才适合旅游党?4个选购技巧统统告诉你!
有没有能让我们防夹3天不在家都不用担心猫咪铲屎问题的方法?当然有了!自动猫砂盆就是最好的选择,要知道,有个好用合适的自动猫砂盆在家的话,根本不用担心生虫发臭的问题出现,因为自动猫砂盆能及时感应到猫咪…...

算法知识点————双指针【删除重复元素】【反转链表】
删除重复元素 题目://给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数 思路:…...

建造者模式builder
此篇为学习笔记,原文链接 https://refactoringguru.cn/design-patterns/builder 能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象...

IEC103设备数据 转 IEC61850项目案例
目录 1 案例说明 1 2 VFBOX网关工作原理 1 3 准备工作 2 4 配置VFBOX网关采集103设备数是 2 5 用IEC61850协议转发数据 4 6 网关使用多个逻辑设备和逻辑节点的方法 6 7 IEC103协议说明 8 8 案例总结 9 1 案例说明 设置网关采集IEC103设备数据把采集的数据转成IEC61850协议转发…...

438.找到字符串中所有字母异位词
题目 链接:leetcode链接 思路分析(滑动窗口) 很容易想到,这个题目要求我们在字符串s中找到一个定长的窗口让窗口里面出现异位词。 OK,先思考一下怎么快速判断两个字符串是否是异位词? 比较简单的方法是…...

Microsoft SC-100: Microsoft 网络安全架构师
SC-100认证介绍 Microsoft SC-100: Microsoft 网络安全架构师是微软网络安全方向的相关证书, 作为 Microsoft 网络安全架构师,你要将网络安全策略转化为保护组织的资产、业务和运营的功能。 你要设计、指导实现和维护遵循零信任原则和最佳做法的安全性解…...

代码随想录训练营day42|188.买卖股票的最佳时机IV,309.最佳买卖股票时机含冷冻期,714.买卖股票的最佳时机含手续费
188.买卖股票的最佳时机IV 变成了最多可以买卖k只股票 class Solution { public:int maxProfit(int k, vector<int>& prices) {vector<vector<int>> dp(prices.size(),vector<int>(2*k1,0));for(int i1;i<2*k1;i2){dp[0][i]-prices[0];}//初始…...
解决Pynput不能在Ubuntu22.04上正常使用问题
pynput 是一个 Python 库,它提供了一套简单的接口来控制和监控输入设备,包括鼠标和键盘。这个库允许开发者编写跨平台的代码来处理输入事件,无需担心底层操作系统的差异。 下面是pynput的测试程序: from pynput.keyboard import …...
IPV4端口数据有哪些?
关于IPv4的端口数据包括端口号,协议类型,端口状态,扫描时间以及开放时间,这些是常见的实时IPv4端口扫描和分析数据。 IPv4端口号 Ipv4端口号是一个16位的无符号整数,范围从0到65535,这些端口号用于区分运…...

【爱加密_云平台-注册/登录安全分析报告】
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…...

Open CASCADE学习|按圆离散旋转体
旋转体是一个非常重要的概念,它涉及到三维空间中由二维曲线绕某一轴线旋转形成的立体形状。这种旋转体的形成过程,实际上是一个连续变化的动态过程,但在数学和几何学中,我们往往通过静态的方式来描述和研究它。 旋转体的基本特性…...

无人矿车使用ZMQ消息代理进行跨机互联进行消息收发
目录 背景 前景知识 1.启动一个代理服务器 2.发布者 3.订阅者 实际应用 1.模拟智驾收来自交互箱131的按钮json数据 2.模拟交互箱131收来自智驾137的cjson数据 背景 在调试防爆无轨无人矿车时,出现消息代理不能很好转发,于是在公司通过代理的方式…...

医疗机构关于DIP/DRG信息化建设
推进DIP/DRG支付方式改革是一项系统性工程,牵一发而动全身。作为河北省DIP试点医院,河北医科大学第二医院将信息化与创新性管理理念融合,用好支付工具做好精细化管理,积极应对改革。 ■ 改革背景 国家医疗保障局制定的《DRG/DIP支…...

100个候选人,没一个能讲明白什么是自动化框架?
什么是自动化测试框架 01 什么是框架 框架是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法。它规定了应用的体系结构,阐明了整个设计、协作构件之间的依赖关系、责任分配和控制流程,表现为一组抽象类以及其实例之间…...

数据结构与算法1: 链表
题目名称: 重排链表 链接: . - 力扣(LeetCode) 介绍:本题的目标是将链表进行重新组合,如下图。 如果按照标准的解法,我们需要实现三步 1. 链表中点的获取 2. 链表的反转 3. 链…...

【专题】2024年8月医药行业报告合集汇总PDF分享(附原数据表)
原文链接:https://tecdat.cn/?p37621 在科技飞速发展的当今时代,医药行业作为关乎人类生命健康的重要领域,正处于前所未有的变革浪潮之中。数智医疗服务的崛起,为医疗模式带来了全新的转变,开启了医疗服务的新时代。…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...