STL 源码剖析 heap堆

  • heap不属于STL容器的组件,属于幕后角色,是priority_queue的助手
  • priority_queue 允许用户以任何次序将任何元素推入容器内,但是取出的时候需要从优先级最高(也就是数值最高)的元素开始取,这种思想是基于heap的函数实现
  • 如果使用list作为 priority_queue 的底层实现机制,元素的插入操作可以使用常数的时间,但是不好找 极值,需要进行线性扫描和遍历;如果考虑到大小这个关键因素,进行元素的排序,随后进行元素之间的衔接,降低了元素的插入效率
  • 使用binary search tree作为priority的底层机制,这样对于元素的插入和取极值就有O(logN)的表现;但是缺点是:1,binary search tree需要具备足够的随机性;2,binary search tree实现很难;
  • binary heap是一种 complate binary tree(完全二叉树),除了最底层叶节点之外都是处于饱和的状态,而最底层的叶节点从左只有不得有空隙,示例如下

  •  complate binary tree(完全二叉树)整棵树内部没有任何的节点漏洞
  • 好处:1,使用array存储所有的节点,保留#0 元素的位置 (将其设为无限大或者无限小),那么当 complate binary tree(完全二叉树)的某个节点位于array数组中的i处的时候,其左子树节点必然位于array的 2i 处,其右子树的节点必然位于array的 2*i+1 的位置。 ( 这里的 / 表示取整数),通过保留#0 的操作,可以使用array
  • 轻而易举实现出complate binary tree。使用数组的方式表达完全二叉树的方式 称为隐式 表述法
  • 考虑到array无法动态的改变大小,使用vector代替array实现 完全二叉树
  • 根据元素的排列方式 heap分为max-heap和min-heap,前者每个节点的键值 都大于或者等于其子节点的键值 ;后者 每个节点的键值都小于或者等于其每个子节点的键值
  • max-heap 最大值在根节点,其总是位于底层的array或者vector的开头;
  • min-heap 最小值在根节点,其总是位于底层的array或者vector的开头;
  • STL 供应的是 max-heap 本文所有的heap都按照 max-heap处理

heap的算法

push_heap 算法

  • 新加入的元素一定是放在最下一层作为叶节点,并填补从左到右的第一个空格,也就是底层vector()的end()处

  •  执行上溯程序,将新节点和父接地那记性比较,如果大于父节点,就进行父子对换,如此一直上溯,直到不需要对换或者直到遇到根节点为止
  • 接收两个迭代器表示heap的底部操作(vector)的头尾,并且新的元素插入到底部容器的最尾端,如果不符合这两个条件,那么push_heap的结果是不可预期的
  • distance_type 方便的决定某个迭代器的类型 参考链接如下
  • STL源码剖析 5中迭代器型别_CHYabc123456hh的博客-CSDN博客
template<class RandomAccessIterator>
inline void push_heap(RandomAccessIterator first,RandomAccessIterator last){//函数被调用的时候 新元素应已经置于底部容器的最尾端//distance_type 用于判断迭代器的类型,从而判定是否进行偏特化操作__push_heap_aux(first,last,distance_type(first),value_type(first));
}template <class RandomAccessIterator,class Distance,class T>
inline void __push_heap_aux(RandomAccessIterator first,RandomAccessIterator last,Distance *,T*){//根据implicit representation heap的结构特性,新值必须置于底部容器的最尾端,即第一个洞号(last-first)-1__push_heap(first,Distance((last-first)-1),Distance(0),T(*(last-1)));
}//以下这组push_back() 不允许指定 "大小比较的标准"
template <class RandomAccessIterator,class Distance, class T>
void __push_heap(RandomAccessIterator first,Distance holeIndex,Distance topIndex,T value){Distance parent = (holeIndex - 1)/2; //找出父节点//当holeIndex并没有到达顶峰的同时 父节点还小于新值 (不符合heap的次序特性)//使用operator < 进行元素的比较,因此STL heap是一种max-heap//父节点是动态变化的//每次是和value进行比较,因此不需要进行父子元素值的对调,直接让子元素接管父亲的数值while(holeIndex > topIndex && *(first + parent) < value){*(first + holeIndex) = *(first + parent); //令洞值为父值,也就是孩子赋予了父亲的数值,父亲位置holeIndex = parent; //调整洞号,向上提升至父节点parent = (holeIndex - 1)/2;//新洞的父节点}//持续至顶端,或者满足 heap的次序特性为止*(first + holeIndex) = value;//令新洞为新的数值,完成插入操作
}

pop_heap() 算法

  • 弹出最大元素,根节点缺失需要使用最末尾的元素进行弥补,然后执行下放程序,将根节点的数值和最大的叶子节点进行互换,以此类推,直到这个洞的键值大于左右两个子节点,或者下放至叶子节点为止

  • pop_heap代码 
//这个堆没有指定 大小比较的标准
template <class RandomAccessIterator,class T,class Distance>
inline void __pop_heap(RandomAccessIterator first,RandomAccessIterator last,RandomAccessIterator result,T value,Distance *){*result = *first; //设定尾值为首值,也就是此刻的尾值为先前的根节点//可以通过底层容器的 pop_back()取出尾值//重新调整 heap 洞号为0(即树根处),将其数值设定为 value(先前的尾节点的数值)__adjust_heap(first,Distance(0),Distance(last-first),value);
}//这个堆没有指定 大小比较的标准
template <class RandomAccessIterator,class Distance,class T>
void __adjust_heap(RandomAccessIterator first,Distance holeIndex,Distance len,T value){Distance topIndex = holeIndex;Distance secondChild = 2 * holeIndex +2;//洞节点的右子节点while(secondChild < len){//比较洞节点的左右两个孩子的数值,然后以secondChild代表较大的子节点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;}*(first + holeIndex) = value;
}
  • pop_heap之后,最大元素将被放置于底部容器的最尾端,并没有被取走。可以使用底部容器提供的back()函数获取这个数值,也可以使用vector提供的pop_back()函数将其移除

sort_heap()

  • pop_heap可以获得heap中键值最大的元素,持续使用pop_heap,不断得到最大的元素,就可以实现排序。需要注意的是每没用一次pop_heap都需要将操作范围从后向前缩减一个元素。
  • sort_heap()函数接收两个迭代器,用来表现一个heap底部容器的头尾。如果不符合这个条件,sort_heap的排序结果未可预期。
  • *排序过后的heap已经被破坏,不再是一个合法的堆了,相当于操作底层的元素,修改了元素,破坏性是无法修改的
template <class RandomAccessIterator>
void sort_heap(RandomAccessIterator first,RandomAccessIterator last){//每执行一次pop_heap(),极值(在STL heap中为极大值)就会被放在尾端//扣除尾端再次执行pop_heap(),次极值又被放在新的尾端,按照上述流程一直执行,最后得到排序结果while (last - first > 1){std::pop_heap(first,last--);//每次执行pop_heap()一次,操作范围即缩减一格}
}

 

make_heap()

  •  这个函数的目的是为了将一段现有的数据将其转化为堆的形式,主要依据就是complate binary tree 的隐式表述
template <class RandomAccessIterator,class T,class Distance>
void __make_heap(RandomAccessIterator first,RandomAccessIterator last,T*,Distance *){if (last - first < 2){ //如果长度为0或者为1,不需要重新排列return;}Distance len = last - first;//找出第一个需要重新排列的子树的头部,以parent标定//考虑到任何节点不需要执行perlocate down,因此使用hole Index进行命名更好Distance parent = (len - 2)/2;while (true){//重新排列以parent为首的子树//len的目的是为了让__adjust_heap() 判断操作的范围__adjust_heap(first,parent,len,T(*(first + parent)));if (parent == 0){return; //走完根节点 结束}parent--; // (即将重拍子树的)头部向前一个节点}
}

heap没有迭代器

  •  heap不提供遍历的功能,也不提供迭代器

测试用例

int main(){int ia[9] = {0,1,2,3,4,8,9,3,5};std::vector<int> i_vec(ia,ia+9);std::make_heap(i_vec.begin(),i_vec.end());for (int i = 0; i < i_vec.size(); ++i) {std::cout << i_vec[i] << ' '; //9 5 8 3 4 0 2 3 1}std::cout << std::endl;i_vec.push_back(7);std::push_heap(i_vec.begin(),i_vec.end());for (int i = 0; i < i_vec.size(); ++i) {std::cout << i_vec[i] << ' '; //9 7 8 3 5 0 2 3 1 4}std::cout << std::endl;std::pop_heap(i_vec.begin(),i_vec.end());std::cout << i_vec.back() << std::endl; //9 return but no removei_vec.pop_back(); //remove last elem and no returnfor (int i = 0; i < i_vec.size(); ++i) {std::cout << i_vec[i] << ' '; //8 7 4 3 5 0 2 3 1}std::cout << std::endl;std::sort_heap(i_vec.begin(),i_vec.end());for (int i = 0; i < i_vec.size(); ++i) {std::cout << i_vec[i] << ' '; //0 1 2 3 3 4 5 7 8}std::cout << std::endl;{//test heap (底层使用array实现)int ia[9] = {0,1,2,3,4,8,9,3,5};std::make_heap(ia,ia+9);//array不能动态的改变大小,因此不可以对满载的array进行push_heap()操作//需要先在array的尾端增加一个元素std::sort_heap(ia,ia+9);for (int i = 0; i < 9; ++i) {std::cout << ia[i] << ' '; //0 1 2 3 3 4 5 8 9}std::cout << std::endl;//经过排序之后的heap 不再是一个合法的heap//重新再做一个heapstd::make_heap(ia,ia+9);std::pop_heap(ia,ia+9);std::cout << ia[8] << std::endl; //8}
}

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

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

相关文章

java 打印星号

代码1 package lesson.l2_for; //6列4行 //****** //****** //****** //****** public class ForDemo8 {public static void main(String[] args) {for (int i1;i<4;i){for (int j 1; j <6 ; j) {System.out.print("*");}System.out.println();}} }代码2 pa…

python从小白到大牛百度云盘_Java从小白到大牛 (关东升著) 中文pdf+mobi版[36MB]

《Java从小白到大牛》是一本Java语言学习立体教程&#xff0c;读者群是零基础小白&#xff0c;通过本书的学习能够成为Java大牛。主要内容包括&#xff1a;Java语法基础、Java编码规范、数据类型、运算符、控制语句、数组、字符串、面向对象基础、继承与多态、抽象类与接口、枚…

java打印九九乘法表

代码1 package lesson.l5_loop; //九九乘法表 //1*11 //2*12 2*24 //3*13 3*26 3*39 //4*14 4*28 4*312 4*416 //5*15 5*210 5*315 5*420 5*525 //6*16 6*212 6*318 6*424 6*530 6*636 //7*17 7*214 7*321 7*428 7*535 7*642 7*749 //8*18 8*216 8*324 8*432 8*540 8*648 8*75…

STL源码剖析 priority_queue

priority_queue是一个拥有权重概念的queue&#xff0c;允许底部加入新的元素&#xff0c;头部删除旧的元素&#xff0c;以及审视元素数值的操作priority_queue带有权重的概念&#xff0c;即元素按照权重进行排列&#xff0c;而不是按照插入队列的顺序进行排序。要求权值高者在前…

python数字1 3怎么表示_Python入门篇之数字

数字类型 数字提供了标量贮存和直接访问。它是不可更改类型&#xff0c;也就是说变更数字的值会生成新的对象。当然&#xff0c;这个过程无论对程序员还是对用户都是透明的&#xff0c;并不会影响软件的开发方式。 Python 支持多种数字类型&#xff1a;整型、长整型、布尔型、双…

STL源码剖析 slist单向链表概述

概述 SGI STL的list是一个双向链表&#xff0c;单向链表是slist&#xff0c;其不在标准规格之内单向和双向链表的区别在于&#xff0c;单向链表的迭代器是单向的 Forward Iterator&#xff0c;双向链表的迭代器属于双向的Bidirectional Iterator。因此很多功能都被受限但是单向…

output怎么用_用树莓派实现室内温度监控

树莓派加上温度传感器实现室内温度监控。可用于家庭&#xff0c;轿车&#xff0c;工业&#xff0c;农业 等许多方面。可做温度预警&#xff0c;自动降温等操作。各位小伙伴可自行脑补发挥。1.硬件准备a.树莓派&#xff08;Raspberry Pi&#xff09;一个b.DS18B20温度传感器一个…

STL源码剖析 关联式容器

STL关联式容器以set(集合) 和 map(映射表)两大类&#xff0c;以及对应的衍生体构成,比如mulyiset(多键集合) multimap(多键映射表) ,容器的底层均基于红黑树RB-Tree也是一个独立的容器&#xff0c;但是不对外开放此外还提供了标准之外的关联式容器 hash table散列表&#xff0c…

STL源码剖析 关联式容器 红黑树

概念 红黑树不仅仅是一个二叉树&#xff0c;必须满足如下条件1&#xff0c;每个节点不是红色就是黑色 (深色底纹为黑色&#xff0c;浅色底纹为红色)2&#xff0c;根节点是黑色的3&#xff0c;如果节点为红&#xff0c;其子节点必须为黑色的4&#xff0c;任一节点至NULL(树的尾…

python生成的词云没有图案_还在为专栏封面发愁?我用Python写了个词云生成器!...

妈妈再也不用担心我写专栏找不到合适的封面了&#xff01;B站专栏的封面至少是我一直头疼的问题&#xff0c;每次写完文章却找不到合适的图片作为封面。 词云是一个很不错的选择&#xff0c;既美观&#xff0c;又提纲挈领。网上也有词云生成的工具&#xff0c;但大多收费/只能试…

java 1000以内的完数

题目 代码 package lesson.l6_review;public class PrefectNumber {public static void main(String[] args) {for (int i 1; i <1000 ; i) {int num0;for (int j 1; j <i-1 ; j) {if (i%j0){numj;}}if (inum){System.out.print(i"\t");}}} }

STL源码剖析 map

所有元素会根据元素的键值自动被排序 元素的类型是pair&#xff0c;同时拥有键值和实值&#xff1b;map不允许两个元素出现相同的键值pair 代码 template <class T1,class T2> struct pair{typedef T1 first_type;typedef T2 second_type;T1 first; //publicT2 secon…

java api接口怎么写_Java 如何设计 API 接口,实现统一格式返回?

来源&#xff1a;老顾聊技术前言接口交互返回格式控制层Controller美观美化优雅优化实现方案前言在移动互联网&#xff0c;分布式、微服务盛行的今天&#xff0c;现在项目绝大部分都采用的微服务框架&#xff0c;前后端分离方式&#xff0c;(题外话&#xff1a;前后端的工作职责…

java 二维数组

声明和初始化 静态初始化 // 静态初始化&#xff1a; // 一维数组int[] arr1_1 {1, 2, 4};System.out.println(Arrays.toString(arr1_1)); // 二维数组int[][] arr1_2 {{1, 2}, {4, 5}, {9, 10}};for (int[] i :arr1_2) {System.out.print(Arrays.toS…

STL源码剖析 hashtable

二叉搜索树具有对数平均时间的表现&#xff0c;但是这个需要满足的假设前提是输入的数据需要具备随机性hashtable 散列表这种结构在插入、删除、搜寻等操作层面上也具有常数平均时间的表现。而且不需要依赖元素的随机性&#xff0c;这种表现是以统计为基础的 hashtable的概述 …

append在python里是什么意思_“一棵绿萝七个鬼”是什么意思?卧室里到底能不能养绿萝!...

很多人都喜欢在家里养盆绿萝&#xff0c;一是能净化室内空气&#xff0c;让家里绿意浓浓&#xff0c;更有生机一些&#xff1b;二是绿萝好养&#xff0c;水培土培都行&#xff0c;养着也省心。在养花界有一句俗语&#xff1a;“一棵绿萝七个鬼”&#xff0c;这句话是什么意思呢…

java 二分查找

注意 二分查找要求原数组为有序序列&#xff0c;从小到大 递归解法 public class problem9 {public static void main(String[] args) {int[] arr {1,2,3,4,6,7};int left 0;int right arr.length - 1;int value 2;System.out.println(Arrays.toString(arr));int index …

java三个柱子汉诺塔问题

题目 移动盘子&#xff0c;每一次只能移动一个&#xff0c;小盘子在大盘子上。 打印1 from A to B过程 注意 1&#xff09;盘子编号的变化和辅助柱子的变化 2&#xff09;当盘子编号为1时&#xff0c;结束递归&#xff0c;此时移动结束 代码 package p2;/*** Illustratio…

java杨辉三角形

题目 代码1 public class YangHuiTriangle {public static void main(String[] args) {print(10);}public static void print(int num) {int[][] arr new int[num][];for (int i 0; i < num; i) { // 第一行有 1 个元素, 第 n 行有 n 个元素arr[i] new int[i…

STL源码剖析 基本算法 equal | fill | iter_awap | lexicographical_compare | max | min | swap |mismatch

Equal 两个序列在[first,last)区间内相等&#xff0c;equal()返回true。以第一个序列作为基准&#xff0c;如果第二序列元素多不予考虑&#xff0c;如果要保证两个序列完全相等需要比较元素的个数 if(vec1.size() vec2.size() && equal(vec1.begin(),vec1.end(),vec2…