索引堆简介

堆这种数据结构还有一个非常重要的应用,就是索引堆。为什么要使用索引堆呢?就像下图所示的那样,当一个混乱的数组被heapify之后,已经成为了一个最大堆。这个heapify之后,原始数组中的元素位置已经发生了变化,既然位置发生了变化,就会导致两个问题:

第一个就是如果数组是一个复杂的数组结构,变换元素的位置会非常消耗资源,相信这个您是能够理解的。

第二个问题,就需要结合实际情况了,假如我们的数组索引代表着进程任务的PID,那如果我们heapify之后,还是变化数组元素(元素应该是进程具体的任务数据)的位置,那如果我想改变原来某一个PID中数据的值,我就找不到这个元素了,或者说,即使找到也不是我要找的那个进程了。

索引堆简介_第1张图片
普通堆

那我们应该怎么解决这个问题呢?——索引堆。什么是索引堆呢:

顾名思义我们要给原始的数组下标建立一个数组,当对数组中的元素进行heapify的时候,变换的不是数组中的元素,而是下标数组。这个时候,我们就可以根据下标的二叉堆,来索引到我们想找的元素。

我这么说可能还是有一些抽象,具体的效果见下图:

索引堆简介_第2张图片
索引堆

具体的过程给您讲清楚了,我们下面来看源码,下图是两个构造函数的源码,其中下方的构造函数就包含heapify的过程。

索引堆简介_第3张图片
构造函数的源码

再看shiftUp和shiftDown的源码,原来的二叉堆这两个操作是对数据进行操作,而索引堆其实是对数组下标的操作。

索引堆简介_第4张图片
shiftUp和shiftDown操作源码

建好索引之后,就会有一个非常常见的操作,修改某一个索引所对应的数据,修改完成之后再重新构建一遍索引,具体的源码见下图。

索引堆简介_第5张图片
change函数的源码

从上图您可以看出来,我们在找传入的索引时,需要遍历索引数组,这种操作是会降低索引堆的时间复杂度的。那我们有没有其他的办法优化查找索引的效率呢,答案肯定是有的。

反向查找表

反向查找表的概念就是:

再建立一个数组,这个数组的下标和原始数据数组的下标的意思是一样的,就是索引的意思。而数组中存储的元素则是索引在索引堆数组中的位置。

具体的示意如下图所示:

索引堆简介_第6张图片
反向查找表

也就是说,上面方向查找表的意思是:第一个元素代表1这个索引在索引堆中的位置为10,第二个元素代表2这个索引在索引堆中的位置为9,以此类推。具体的源码如下图所示:

索引堆简介_第7张图片
构造函数的源码-初始化反向查找表

如上图所示,还是两个构造函数的源码,不同的构造函数,对反向查找表的初始化是不一样的。

索引堆简介_第8张图片
shiftUp和shiftDown的源码-维护反向查找表

如上图所示,再看shiftUp和shiftDown的源码,对反向查找表的维护就是,将索引堆中的值取出来(值就是索引值),这个值就是方向查找表的下标,那这个下标应该对应的元素就是索引堆中的位置。

索引堆简介_第9张图片
change函数的源码-使用反向查找表

最后是change源码的修改,当我们把反向查找表维护好之后,那change函数的源码还可以用一句特别喜欢的短语来形容:即短小,又精悍。

我是徐建航,这是我写的第65篇文章,欢迎你加入007社群,七天写一篇,一起写七年,七年之后一起去南极。

你可能感兴趣的:(索引堆简介)