堆的概念与特征
堆:将一组数据按照完全二叉树的存储顺序,将数据存储在一个一维数组中的结构。
堆分为大顶堆,小顶堆。
大顶堆:任意节点的值均大于等于它的左右孩子,并且最大值位于堆顶。
小顶堆:任意节点的值均小于等于它的左右孩子,并且最小值位于堆顶。
优先队列和堆不是同一个level的概念,但是Java的PriorityQueue就是堆实现的,因此在Java领域可以认为堆就是优先队列,优先队列就是堆,换做其他场景则不行。
堆的构造过程
如何建立一个大堆:
-
int i = (size-2)/2 找到最后一个。
-
比较,比父节点大就交换。小则不动。
-
i-1,继续比较。
-
若交换后,子树却不满足了,所以还需调整它的子树。
-
直到根节点也满足要求了,就完毕了。
插入操作
放到最后一个位置,然后上浮,保证子树都满足堆。
删除操作
堆本身比较特殊,一般对堆中的数据进行操作都是针对堆顶的元素。即每次都从堆中获得最大值或最小值,其他的不关心,所以我们删除的时候,也是删除堆顶。
如果直接删除堆顶,整个结构被破坏了。
实际策略,将堆最后一个元素和堆顶元素交换,然后删除堆中最后一个元素。然后再维持一下堆,上浮下沉。
总结
堆价值就在于大顶堆的根节点是整个树最大的那个,增加时会根据根的大小来决定要不要加,而删除操作只删除根元素。这个特征可以在很多场景下有奇妙的应用,后面的算法题全都基于这一点。
堆的效率高。因为堆元素的数量是有限制的,一般不用将所有的元素都放到堆里。后面题目中可以看到,在序列中找K大,则堆的大小就是K。如果K个链表合并,那么堆就是K。
口诀:
查找:找大用小,大的进;找小用大,小的进。
排序:升序用小,降序用大。
查找的方法就是:找 k 大, 则用小顶堆,后序的数据只有比根元素更大时才允许进入堆。如果找 k 小,则反过来。