chapter4 序列式容器:heap


1 概述

heap并不是归属于STL容器组件,它是个幕后英雄,扮演priority queue的助手。顾名思义,priority queue允许用户以任何次序将任何元素推入容器内,但取出时一定是从优先权最高(也就是数值最高)的元素开始取。binary max heap正是具有这样的特性,适合作为priority queue的底层机制。

1.1 priority queue底层机制的选择分析

  1. 若以list作为priority queue的底层机制,元素插入操作可享常数时间;但是要找到list中的极值,却需要对整个list进行线性扫描。如果在插入元素之前先经过排序这一关,使得list的元素值总是由小到大(或由大到小),此时取得极值以及元素删除操作虽然能达到最高效率,可元素的插入却只有线性表现;
  2. 若以binary serach tree作为priority queue的底层机制,则元素的插入和极值的取得就有O(logN)的表现。但binary serach tree的输入需要足够的随机性并且binary serach tree也不容易实现。
  3. binary heap应运而生,使得priority queue的复杂度介于queuebinary search tree之间。

1.2 binary heap

binary heap是一种complete binary tree(完全二叉树)。整颗binary tree除了最底层的叶节点之外,是填满的,而且最底层的叶节点由左至右又不得有空隙。
在这里插入图片描述
隐式表述法:利用array 来储存所有节点。假设动用一个小技巧,将array的#0元素保留(或设为无限大值或无限小值),那么当complete binary tree中的某个节点位于array的i处时,其左子节点必位于array的2i处,其右子节点必位于array的2i+1处,其父节点必位于i/2处(此处的“/”权且代表高斯符号,取其整数)。通过这么简单的位置规则,array可以轻易实现出complete binary tree

由于array无法动态改变大小,而heap却需要这项功能,因此,以vector代替array是更好的选择!

1.3 heap的分类

根据元素排列方式,heap可分为max-heap和min-heap两种,前者每个节点的键值(key)都大于或等于其子节点键值,后者的每个节点键值(key)都小于或等于其子节点键值。因此,max-heap的最大值在根节点,并总是位于底层array或vector的起头处;min-heap的最小值在根节点,亦总是位于底层array或vector的起头处。STL供应的是max-heap

2 heap算法

2.1 push_heap

为了满足complete binary tree的条件,新加入的元素一定要放在最下一层作为叶子节点,并填补从左至右的第一个空格,也就是把新元素插入在底层vector的end()处。

新元素是否适合于其现有位置呢?

为满足max-heap的条件(每个节点的键值都大于或等于其子节点键值),需要执行一个所谓的 percolate up(上溯)程序:
将新节点拿来与其父节点比较,如果其键值(key)比父节点大,就父子对换位置。如此一直上溯,直到不需对换或直到根节点为止。

在这里插入图片描述
push_heap算法最终调用__push_heap函数:

template<class RandomAccessIterator, class Distance, class T>
void __puah_heap(RandomAccessIterator first, Distance holeIndex, Distance topIndex, T value) { //holeIndex:容器的最尾端
	Distance parent = (holeIndex - 1) / 2; //找出父节点
    while(holeIndex > topIndex && *(first + parent) < value) {
        //当尚未到达顶端,且父节点小于新值(此时不满足heap的次序特性)
        *(first + holeIndex) = *(first + parent); //令洞值为父值
        holeIndex = parent;	//percolate up(上溯):调整洞号,向上提升至父节点
        parent = (holeIndex - 1) / 2; //新洞的父节点
    } //持续至顶端,或满足heap的次序特性为止
    *(first + holeIndex) = value; //令洞为新值,完成插入操作
}   

2.2 pop_heap

作为max-heap,最大值必然在根节点。pop操作取走根节点(其实是将原根节点设至底部容器vector的尾端节点)后,为了满足complete binary tree的条件,必须割舍最下层最右边的叶节点,并将其值重新安插至max-heap(因此有必要重新调整heap结构)

为满足max-heap的条件(每个节点的键值都大于或等于其子节点键值),需要执行一个所谓的 percolate down(下溯)程序:将空间节点和其较大子结点“对调”,并持续下放,指至叶节点为止。然后将前述割舍节点设给这个“已到达叶层的空洞节点”,再对它执行一次上溯程序。

在这里插入图片描述
需要注意的是,在经过pop_heap后,最大元素只是被放置于底部容器的最尾端,尚未被取走。如果要取其值,可使用底部容器(vector)所提供的back()操作函数。如果要移除它,可使用底部容器(vector)所提供的pop_back()操作函数。

2.3 sort_heap

通过pop_heap将heap中键值最大的元素取出并放置在底层容器的最尾端。因此不断对heap进行pop操作,便可达到排序效果。循环的终止条件为last-first > 1

2.4 make_heap

基于complete binary tree的隐式表述,将一段现有的vector数据转化为heap。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值