美文网首页
第二十八节-堆

第二十八节-堆

作者: wean_a23e | 来源:发表于2018-12-06 23:19 被阅读11次

    堆的定义

    • 堆是一颗完全二叉树
    • 堆中每个节点都必须大于等于(或者小于等于)其子树中每个节点的值。

    如何实现一个堆

    实现堆主要依靠堆化操作。而堆化操作又分为向上堆化和向下堆化。

    就时间复杂度来说,向下堆化的操作次数少一些,所以,若是给定一个数组进行堆化,优先选择向下堆化。

    若是不知道事先要存放多少个数据,需要动态建堆,那就采用向上堆化。

    Java 中的 PriorityQueue 就应用了向上和向下两种堆化方式:

    • 构造 PriorityQueue 时,若是直接给定一个 collection ,则对给定的 collection 进行向下堆化建堆。
    • 往 PriorityQueue 中插入记录时,就把插入数据放在数组末尾,进行向上堆化。

    堆排序

    堆排序是一种时间复杂度为 O(nlogn) 的 原地排序 算法。堆排序的过程分为两步,先建堆,后排序。

    • 建堆的操作,时间复杂度是 n。可以通过数学计算,算出建堆的时间复杂度其实是 O(n)。
    • 排序:其实就是重复删除堆顶元素的过程。时间复杂度是 O(nlogn)。

    所以,堆排序的总时间复杂度是 O(nlogn)。

    为什么实际开发中,人们更倾向于使用快排,而不是堆排序?

    快排和堆排序,都是时间复杂度 O(nlogn),都是原地排序算法,都是不稳定的排序算法。看起来真是十分有缘分,但是,真较真起来,要排序的话,还是比较倾向于使用快排。原因如下:

    • 第一点,堆排序不能友好利用 cpu 的缓存机制

    快排对数据的访问,是顺序访问的,可以很好地利用 CPU 的缓存机制,在 IO 上节省大量时间。

    而堆排序对数据是跳着访问的,比如对堆顶元素进行堆化,会依次访问数据下标是 1, 2, 4, 8……的数据,是对 CPU 的缓存机制不友好的。

    • 第二点,对于同样的数据,在排序过程中,堆排序算法的交换次数要多于快速排序

    堆排序的过程,需要经历建堆的过程,这个过程会增加数组的逆序度。假设数组原本是有序的,建堆操作后,数组反而是无序的。

    这个过程,会让堆排序在排序时,比快速排序进行数据交换的次数更多。

    堆的应用

    虽然堆排序不如快速排序。但是特定数据结构服务特定场景。堆也有适合自己的应用场景:

    • topk
    • 优先级队列
    • 多路归并排序
    • 流里面的中位数
    • 流里面的中值

    相关文章

      网友评论

          本文标题:第二十八节-堆

          本文链接:https://www.haomeiwen.com/subject/bonacqtx.html