携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
小顶堆
堆是一种特殊的树,只要满足以下两个条件,它就是一个堆:
- 堆是一颗完全二叉树
- 堆中某个节点的值总是不大于(或不小于)其父节点的值
其中,根节点最大的堆叫做大顶堆,根节点最小的对叫做小顶堆
满二叉树:所有层都达到最大节点数 1、2、4、8
完全二叉树:除了最后一层外其他层达到最大节点数,且最后一层节点都靠左排列
完全二叉树最适合用数组做存储,因为它的节点都是紧凑的,且只有最后一层节点数不满
下标为0的位置不存储元素,因为方便我们快速找到父节点(下标/2)。比如8的父节点5/2=2
。
插入元素
尾部插入,然后上浮,插入元素后,我们需要继续满足堆的特性, 插入元素后不一定满足堆的特性,我们就需要堆化。
在完全二叉树中,插入的节点与它的父节点相比,如果比父节点小,就交换他们的位置,交换后再比较再交换,知道笔父节点大为止。这就是插入元素时进行的堆化,也叫自下而上的堆化。
从插入元素的过程,我们知道每次与n/(2^x)
相比较,所以插入元素的时间复杂度为O(log n)
。
删除堆顶的元素
尾部(最大的元素)放到堆顶,然后下沉,删除元素后,我们需要继续满足堆的特性, 删除元素后不一定满足堆的特性,我们就需要堆化。
在完全二叉树中,把最后一个节点放到堆顶,然后与左右子节点中小的交换位置(最为是小顶堆)依次往下,知道比左右子节点都小为止(或者没有子节点)。这就是删除元素时进行的堆化,也叫自上而下的堆化。
从删除元素的过程,我们知道每次与2n
和2n+1
相比较,所以删除元素的时间复杂度也为O(log n)
。
建堆
- 空出数组下标为0的节点
- 然后依次执行插入堆的操作,然后堆化
- 直到将所有元素都插入到堆中为止
堆排序
对于小顶堆,堆顶的元素本来就是最小的,依次从堆顶删除元素,然后堆化,直到去除所有元素为止。
删除一个元素的时间复杂度为O(log n)
,那么删除n个元素是:
log n + log(n-1) + ... + log2 + log 1 <= nlog n
而且这样排序不需要占用额外的空间,只需要交换元素时的一个临时变量,所以堆排序的空间复杂度为O(1)
如何应用到定时中
- 我们可以认为每一个任务的过期时间作为小顶堆的元素
- 然后我们将所有任务按照过期时间插入到堆中
- 接着从堆中按照过期时间取出元素,然后执行
- 一直循环