什么是堆
- 堆是一颗【完全二叉树】
- 堆的所有【根节点】“大于”【子节点】
这里的大于是可以定义的。
<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/image-20200318211957067.png" alt="image-20200318211957067" style="zoom:50%;" />
上图所示,都是满足堆上方的性质,一颗完全二叉树,所有的根节点大于子节点
上方展示的为最大堆(相应的也可以定义最小堆)
- 使用数组表示
<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/image-20200318212734956.png" alt="image-20200318212734956" style="zoom:50%;" />
因为堆满足完全二叉树的定义,所以堆可以使用数组来表示【上图所示】。
由上图得在 index 位置上的节点可以推倒出如下公式
parent(i) = i / 2
left child (i) = 2 * i
right child (i) = 2 * i + 1
但是在上图中,其实是浪费了数组的零号位置,如果元素从0号位置排将会是下面的结构
<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/image-20200318213435336.png" alt="image-20200318213435336" style="zoom:50%;" />
由上图可以推倒公式为:
parent(i) = (i - 1) / 2
left child (i) = 2 * i + 1
right child (i) = 2 * i + 2
堆中的常用操作
- Sift Up (向堆中添加元素)
上浮,因为堆底层实现为数组,所以我们在添加元素的时候是直接向数组的末端添加元素,这样就始终保证堆是一个完全二叉树,但是我们需要维持二叉树第二个性质,根节点元素大于子节点,所以我们需要 sift up 操作
假设有如下堆结构:
<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/image-20200318214332416.png" alt="image-20200318214332416" style="zoom:50%;" />
假设我们现在需要添加的元素为 52,现在元素的位置为
arr[arr.length - 1]
,但是这样就违反了堆的结构,因为52 > 16
,sift up 就是如果当前元素大于根节点元素的值那么就交换两个元素,【迭代】执行,直到满足子节点小于根节点。这时我们就需要交换 52 和 16号元素的值
<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/image-20200318214919048.png" alt="image-20200318214919048" style="zoom:50%;" />
交换完成后是这个样子
<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/image-20200318215227768.png" alt="image-20200318215227768" style="zoom:50%;" />
但是这时候又会发现还是不满足堆结构,因为
52 > 41
,所以 52 和 41 还需要交换<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/image-20200318215332914.png" alt="image-20200318215332914" style="zoom:50%;" />
交换后才又满足堆的结构
<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/image-20200318215450242.png" alt="image-20200318215450242" style="zoom:50%;" />
上面 52 号元素移动的整个过程,称之为 sift up 上浮
<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/heap_sift_up.png" style="zoom:80%;" />
- Sift Down (取出堆中的最大元素)
由堆的特性所得,根节点的元素为堆中的最大元素,所以我们只需要取出根节点即可。
<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/image-20200318220445853.png" alt="image-20200318220445853" style="zoom:50%;" />
但是如果直接取出根节点就会导致将原来的堆切割为两个堆,后续在合并的时候就会变的异常麻烦,所以我们这里转变一下思路,直接将末尾的元素 arr[arr.length - 1] 16 与 根节点62 交换位置,而后再将其处理为堆结构这样会简单很多
<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/image-20200318220722116.png" alt="image-20200318220722116" style="zoom:50%;" />
如上图所示,直接将末尾的元素替换掉头结点。此时就违反了堆结构,我们就需要进行 Sift Down 的操作。
当前元素与它的左右孩子进行对比,与左右孩子中较大的孩子进行交换,迭代进行,最终便可完成 Sift Down
<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/image-20200318221110695.png" alt="image-20200318221110695" style="zoom:50%;" />
第一次交换 16 和 50 ,因为 52 > 30,交换后的效果为
<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/image-20200318221300114.png" alt="image-20200318221300114" style="zoom:50%;" />
第二次 交换 16 和 42,因为 42 > 16
<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/image-20200318221601439.png" alt="image-20200318221601439" style="zoom:50%;" />
经过前面的两次交换后,现在就满足最大堆的性质了。
上面两次交换的过程就称为 Sift Down
<img src="https://gitee.com/xiaoxiunique/picgo-image/raw/master/atips/heap_sift_down.png" style="zoom:80%;" />
- replace
- heapity
本文由博客群发一文多发等运营工具平台 OpenWrite 发布
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。