定时器实现原理——小顶堆

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

小顶堆

堆是一种特殊的树,只要满足以下两个条件,它就是一个堆:

  • 堆是一颗完全二叉树
  • 堆中某个节点的值总是不大于(或不小于)其父节点的值

其中,根节点最大的堆叫做大顶堆,根节点最小的对叫做小顶堆

满二叉树:所有层都达到最大节点数 1、2、4、8

完全二叉树:除了最后一层外其他层达到最大节点数,且最后一层节点都靠左排列

完全二叉树最适合用数组做存储,因为它的节点都是紧凑的,且只有最后一层节点数不满

下标为0的位置不存储元素,因为方便我们快速找到父节点(下标/2)。比如8的父节点5/2=2

插入元素

尾部插入,然后上浮,插入元素后,我们需要继续满足堆的特性, 插入元素后不一定满足堆的特性,我们就需要堆化。

在完全二叉树中,插入的节点与它的父节点相比,如果比父节点小,就交换他们的位置,交换后再比较再交换,知道笔父节点大为止。这就是插入元素时进行的堆化,也叫自下而上的堆化。

从插入元素的过程,我们知道每次与n/(2^x)相比较,所以插入元素的时间复杂度为O(log n)

删除堆顶的元素

尾部(最大的元素)放到堆顶,然后下沉,删除元素后,我们需要继续满足堆的特性, 删除元素后不一定满足堆的特性,我们就需要堆化。

在完全二叉树中,把最后一个节点放到堆顶,然后与左右子节点中小的交换位置(最为是小顶堆)依次往下,知道比左右子节点都小为止(或者没有子节点)。这就是删除元素时进行的堆化,也叫自上而下的堆化。

从删除元素的过程,我们知道每次与2n2n+1相比较,所以删除元素的时间复杂度也为O(log n)

建堆

  • 空出数组下标为0的节点
  • 然后依次执行插入堆的操作,然后堆化
  • 直到将所有元素都插入到堆中为止

堆排序

对于小顶堆,堆顶的元素本来就是最小的,依次从堆顶删除元素,然后堆化,直到去除所有元素为止。

删除一个元素的时间复杂度为O(log n),那么删除n个元素是:
log n + log(n-1) + ... + log2 + log 1 <= nlog n

而且这样排序不需要占用额外的空间,只需要交换元素时的一个临时变量,所以堆排序的空间复杂度为O(1)

如何应用到定时中

  • 我们可以认为每一个任务的过期时间作为小顶堆的元素
  • 然后我们将所有任务按照过期时间插入到堆中
  • 接着从堆中按照过期时间取出元素,然后执行
  • 一直循环

猜你喜欢

转载自juejin.im/post/7126868926192943118