![算法训练营:海量图解+竞赛刷题(进阶篇)](https://wfqqreader-1252317822.image.myqcloud.com/cover/239/47379239/b_47379239.jpg)
1.2 优先队列
原理1 优先队列的实现原理
在算法设计中经常需要从序列中查找最值(最小值或最大值),例如最短路径、哈夫曼编码等都需要查找最小值。若顺序查找最值需要O(n)时间,而使用优先队列(priority queue)查找最值只需O(1)时间,则入队和出队需要O(logn)时间。
在树形结构中有两种比较特殊的二叉树:满二叉树和完全二叉树。
满二叉树:指一棵深度为k且有2k-1个节点的二叉树。满二叉树的每一层都“充满”节点,达到最大节点数。
![](https://epubservercos.yuewen.com/B221B0/26763902601483406/epubprivate/OEBPS/Images/40886-00-31-1.jpg?sign=1739361682-SF0gSbmgUzh1toAiFJHktzJPPBQk8oM0-0-ae7900829ec924b5599a328fb4e447bf)
完全二叉树:除了最后一层,每一层都是满的(达到最大节点数),最后一层节点是从左向右出现的。深度为k的完全二叉树,其每个节点都与深度为k的满二叉树中的节点一一对应。完全二叉树和上图中的满二叉树节点一一对应。完全二叉树除了最后一层,前面每一层都是满的,最后一层必须从左向右排列。也就是说,若2没有左子节点,则2不可以有右子节点,若2没有右子节点,则3不可以有左子节点。
![](https://epubservercos.yuewen.com/B221B0/26763902601483406/epubprivate/OEBPS/Images/40886-00-31-2.jpg?sign=1739361682-TKwFz83xFdGVwqrvySqmmC77mIeBjF4P-0-5a95efccead0b4db354879f784e3c256)
性质:若对完全二叉树从上至下、从左至右编号,则编号为i的节点其左子节点编号必为2i,其右子节点编号必为2i+1;其双亲编号必为i/2。
例如,有一棵完全二叉树,2号节点的双亲节点为1,左子节点为4,右子节点为5;3号节点的双亲节点为1,左子节点为6,右子节点为7。
![](https://epubservercos.yuewen.com/B221B0/26763902601483406/epubprivate/OEBPS/Images/40886-00-32-1.jpg?sign=1739361682-Mf8JpjhATSgtVIl2MLyOxsHhEuAr7YAl-0-1be568696c426ffa87f62184b893ec06)
若每一个节点的值都大于或等于左右子节点的值,则称之为最大堆(大顶堆或大根堆);若每一个节点的值都小于或等于左右子节点的值,则称其为最小堆(小顶堆或小根堆)。可以将堆看作一棵完全二叉树的顺序存储结构,一个数据元素序列及其对应的完全二叉树如下图所示,该完全二叉树满足最大堆的定义。
![](https://epubservercos.yuewen.com/B221B0/26763902601483406/epubprivate/OEBPS/Images/40886-00-32-2.jpg?sign=1739361682-ESbDjj0yzPMB6nTv8121TE4k5qaiZwWw-0-67939eeadd0f11dac99d48f39bd64ee0)
普通队列是先进先出的,优先队列与普通队列不同,每次出队时都按照优先级顺序出队。优先队列是通过堆实现的,优先队列中的元素存储满足堆的定义。上图中每一个节点的值都大于或等于左右子节点的值,满足最大堆的定义,是最大值优先的最大堆。
优先队列有出队和入队两种基本操作。
1. 出队
出队时,堆顶(队头)出队,最后一个元素代替堆顶的位置,重新调整为堆。
完美图解:
一个最大堆如下图所示。出队时,堆顶30出队,最后一个元素12代替堆顶。
![](https://epubservercos.yuewen.com/B221B0/26763902601483406/epubprivate/OEBPS/Images/40886-00-33-1.jpg?sign=1739361682-al74ufgVAzNstWiDOiu3Yjw333QbnERX-0-37a697ab67851b2aca4f2ff9c19c93b2)
出队后,除了堆顶,其他节点都满足最大堆的定义,只需堆顶执行下沉操作,即可调整为堆。下沉指堆顶与左右子节点比较,若比子节点大,则已调整为堆;若比子节点小,则与较大的子节点交换,交换到新的位置后继续向下比较,从根节点一直比较到叶子。
堆顶下沉的过程如下。
(1)堆顶12和两个子节点28、20比较,比子节点小,与较大的子节点28交换。
(2)12再和两个子节点16、18比较,比子节点小,与较大的子节点18交换。
(3)比较到叶子时停止,已调整为堆。
![](https://epubservercos.yuewen.com/B221B0/26763902601483406/epubprivate/OEBPS/Images/40886-00-33-2.jpg?sign=1739361682-tpIM4GzqfYZlX37TOUvDtXYppaXRYQjP-0-2df4dc4bf2241060890c6d1dd2e8f7c7)
调整堆的过程就是堆顶从根下沉到叶子的过程。
算法代码:
![](https://epubservercos.yuewen.com/B221B0/26763902601483406/epubprivate/OEBPS/Images/40886-00-33-3.jpg?sign=1739361682-lPEaWrTL3Bobt5jMQ7AvQNp95sKxBnkt-0-f639b8b091593dd7915e6ceb627566cf)
2. 入队
入队时,将新元素放入最后一个元素之后,重新调整为堆。
完美图解:
例如29入队,首先将29放入最后一个元素12的后面。
![](https://epubservercos.yuewen.com/B221B0/26763902601483406/epubprivate/OEBPS/Images/40886-00-34-1.jpg?sign=1739361682-pCp3gGrCAhHhfOgPgXB18aBwSD0FpVNu-0-ef01966279a96cc9af71da1690a82669)
入队后除了新入队的元素,其他节点都满足最大堆的定义,只需新元素执行上浮操作,即可调整为堆。上浮指新元素与其父节点比较,若小于或等于父节点,则已调整为堆;若比父节点大,则与父节点交换,交换到新的位置后,继续向上比较,从叶子一直比较到根。
新元素上浮的过程如下。
(1)新元素29和其父节点18比较,比父节点大,与父节点交换。
![](https://epubservercos.yuewen.com/B221B0/26763902601483406/epubprivate/OEBPS/Images/40886-00-34-2.jpg?sign=1739361682-MvysZMsrGFHJgFCx059uOrgwS4LZZEne-0-9a175cc95fb5ba26ad03bde4743f3339)
(2)29再和其父节点28比较,比父节点大,与父节点交换。
![](https://epubservercos.yuewen.com/B221B0/26763902601483406/epubprivate/OEBPS/Images/40886-00-35-1.jpg?sign=1739361682-RxMVQpQQEjYYSPQdmirGBuBRnEepZYtG-0-ac5da8661509851f7b8f2468ed85eaa1)
(3)29再和其父节点30比较,比父节点小,已调整为堆。
算法代码:
![](https://epubservercos.yuewen.com/B221B0/26763902601483406/epubprivate/OEBPS/Images/40886-00-35-2.jpg?sign=1739361682-NxaGjbsUd09iRtbcpqSdP4N6EosqOhMt-0-35ce0e87a9de023ea70f1308faaaa46c)
3. 算法分析
优先队列是利用堆实现的一种特殊队列,堆是按照完全二叉树顺序存储的,有n个节点的完全二叉树的高度为+1。出队时,堆顶元素出队,最后一个元素代替堆顶,新的堆顶从根下沉到叶子,最多达到树的高度,时间复杂度为O(logn);入队时,新元素从叶子上浮到根,最多达到树的高度,时间复杂度也为O(logn)。