堆 续1

--------------------siwuxie095

  

  

  

  

  

  

  

  

  

堆的基本存储

  

  

在堆中实现的插入操作和删除操作,都是logN 级别的,

显然,堆一定相应的是一个树形结构,最为经典的一种

堆的实现叫做二叉堆(Binary Heap)

  

  

二叉堆对应的是二叉树,所谓二叉树,就是每一个节点,

最多有两个子节点

  

  

那么这个二叉树有什么特点呢?分为两种情况:

  

(一)最大堆

  

1)它的任何一个节点都不大于它的父节点

2)它必须是一棵完全二叉树

  

满足上面两个性质的二叉树,称为最大堆

  

也即

  

1)堆中某个节点的值总是不大于其父节点的值

2)堆总是一棵完全二叉树

  

  

  

(二)最小堆

  

1)它的任何一个节点都不小于它的父节点

2)它必须是一棵完全二叉树

  

满足上面两个性质的二叉树,称为最小堆

  

也即

  

1)堆中某个节点的值总是不小于其父节点的值

2)堆总是一棵完全二叉树

  

  

  

『所谓完全二叉树,即除了最底层,其它层的节点

都被元素填满,且最底层尽可能地从左到右填入』

  

  

  

  

  

下面以最大堆为例进行介绍:

  

  

最大堆在计算机上的一种实现:用数组存储二叉堆

  

堆 续1

  

  

可能很多人都曾经实现过一棵树,觉得既然堆是一个树形结构,就应该

采用链表的形式,设立一个节点,这个节点有左右两个指针分别指向它

的左右孩子,这样可以实现一个堆

  

不过,对于堆而言,有一个非常经典的实现,这种实现方式是使用数组

来存储一个二叉堆

  

之所以可以使用数组来存储一个二叉堆,正是因为堆是一棵完全二叉树

  

  

  

对这颗完全二叉树,自上到下自左到右的给每一个节点标上一个序列

号,即依照层序自上到下、之后在每一层自左到右的标上***

  

「***索引」

  

  

如果第一个节点(根节点)的索引为1,则:

  

对于每一个节点来说,相应的左孩子的索引就是自身索引的二倍,

而相应的右孩子的索引就是自身索引的二倍加一

  

0

1

2

3

4

5

6

7

8

9

10

-

62

41

30

28

16

22

13

19

17

15

  

注意:索引 0 是不使用的

  

  

通过如下公式,即可找到每个元素的双亲和左右孩子:

  

1parent ( i ) = i / 2

(2)left child ( i ) = 2 * i

(3)right child ( i ) = 2 * i + 1

  

  

  

如果第一个节点(根节点)的索引为 0,则:

  

对于每一个节点来说,相应的左孩子的索引就是自身索引的二倍加一,

而相应的右孩子的索引就是自身索引的二倍加二

  

0

1

2

3

4

5

6

7

8

9

62

41

30

28

16

22

13

19

17

15

  

  

通过如下公式,即可找到每个元素的双亲和左右孩子:

  

1parent ( i ) = ( i - 1 ) / 2

(2)left child ( i ) = 2 * i + 1

(3)right child ( i ) = 2 * i + 2

  

  

  

  

 

如何向最大堆中添加元素(即 插入)?

  

这里用到了一个核心操作,通常叫做 Shift Up

  

  

这个堆使用了数组进行表示,所以相应的,在最大堆中添加元素

就相当于在数组的末尾添加元素,但此时的堆可能不满足最大堆

的定义

  

所以要进行一系列的操作来维护堆的定义,具体应该怎么做呢?

  

其实这个过程非常简单,只需要把新加入的元素调整到一个合适

的位置,使得整个二叉树依然保持最大堆的性质

  

具体来说:将新加入的元素和父节点的元素进行比较,如果父节

点的元素比新加入的元素还要小,就交换位置

  

  

新加入的元素逐渐上移的过程,就是Shift Up 的过程

  

  

  

如何从最大堆中取出元素(即 删除)?

  

这里用到了一个核心操作,通常叫做 Shift Down

  

  

如果要想从堆中取出元素,只能取出根节点的那个元素,对于最

大堆来说,就相当于取出了优先级最大的那个元素

  

此时,整个堆中相当于少了一个元素,怎么填补这个元素呢?

  

只需要把整个堆的最后一个元素放到整个堆的第一个元素(根节

点)的位置即可,但此时的堆可能不满足最大堆的定义

  

实际上是互换了位置,之后计数器count--,无法访问原根节点

现在所在的位置,相当于少了一个元素,即被取出了

  

所以要进行一系列的操作来维护堆的定义,具体应该怎么做呢?

  

其实这个过程非常简单,只需要把此时根节点的元素调整到一个

合适的位置,使得整个二叉树依然保持最大堆的性质

  

具体来说:比较左右孩子的元素,谁大就和谁交换位置

  

  

根节点的元素逐渐下移的过程,就是Shift Down 的过程

  

  

  

  

  

  

  

最小堆的情况类似 …

  

  

  

  

  

  

  

  

  

  

【made by siwuxie095】