堆 - 堆的应用

堆有三个典型的应用场景:实现优先队列、求 Top K 、求中位数

实现优先队列

优先队列:队列的性质是先进先出,但是优先队列的行为有些不同。在优先级队列中,出队操作会将优先级最高的元素出队,而不是将最先进入队列的元素出队。
优先级队列的实现方法很多,而堆这种数据结构刚好完全契合优先级队列的性质,所以很多优先级队列都是通过堆实现的。
优先级队列的应用很多,比如之后我们要学习的哈夫曼编码、图的最小生成树算法、图的最短路径算法等,都需要使用到优先队列。

利用堆求Top K

举个例子,现在我们要在一组数据中找出最大的 10 个数据,你有什么好的想法吗?
最简单的想法就是堆数据进行排序,然后取出最大的 10 个数据即可。当然这种方法也可以实现功能,但是在面对动态数据的时候就会变得复杂。

这里我们介绍使用堆实现这个功能的方法,我们假设要维护一个Top 10 的榜单,数据是动态数据:
首先,我们需要维护一个规模为 10 小顶堆,堆内存储着数据前 10 大的数据。当有新数据进入时,可以用这个数据与堆的堆顶进行比较,如果新数据小于堆顶,则对堆来说没有意义,可以直接无视;如果数据大于堆顶,就将堆顶元素删除,然后将新元素插入。
这样,我们会一直维护一个Top K 的堆,如果有程序询问其内容,只需要将堆内元素输出即可。

求中位数

你依然可以使用排序算法对静态数据求中位数,但是面对插入频繁的动态数据,排序的代价就比较高了。

在这里,我们可以使用两个堆完成求中位数的问题:设置一个大顶堆和一个小顶堆,大顶堆中存储较小的一半数据,小顶堆中存储较大的另一半数据,在需要返回中位数的时候,我们只需要返回大顶堆的堆顶数据即可。

在动态数据插入的过程中,我们需要有两个步骤维护堆:插入数据 和 维护两个堆的数据量,我们先看插入操作:
当有新数据插入时,与大顶堆比较,如果新数据小于堆顶元素,那么将这个数据加入到大顶堆中。否则,将数据插入到小顶堆中。

你会发现,由于数据是不确定的,所以插入操作可能会破坏两个堆的数据平衡,这时,就需要维护两个堆的数量
计算两个堆应该保持的数量关系,如果大顶堆中数据过多,就将其堆顶元素删除,并插入到小顶堆中。反之亦然。

这样,我们就通过两个堆实现了求中位数的功能,你可以思考一下,如果求 四分之一分位数 该怎么办?


以让就是堆应用的全部内容

注:本文章的主要内容来自我对极客时间app的《数据结构与算法之美》专栏的总结,我大量引用了其中的代码和截图,如果想要了解具体内容,可以前往极客时间

你可能感兴趣的:(堆 - 堆的应用)