二叉堆
这个二叉堆和先进后出的那个堆不是一个。
而且这个二叉堆从下标1开始存储元素。
这里的二叉堆是个数组,也可以看作是一个完全二叉树,除了最底层外,这颗二叉树是完全充满的。
堆数据结构:利用数组模拟了一个完全二叉树。(这里的堆不是那个先进后出的数据结构)
将树的根节点存放在数组的[1]
位置。第二层的连个存放在[2]
`[3]`,第三层存放在`[4]`[7]
……
所以可以得到这样的规律:
数组中每个元素(假设在数组中的下标为k
)在二叉树中的左节点存放在数组的[2k]
中,而右节点存放在[2k+1]
中。
通过数组来模拟一个 完全二叉树,从而实现排序的算法:堆排序
堆排序
二叉堆分为两种:最大二叉堆和最小二叉堆:
- 最大二叉堆(下面都以此为例)
每个节点[k]
的值都比其左节点[2k]
和右节点[2k+1]
要大。但是,左右这两个节点之间的大小是不确定的,可以随意 - 最小二叉堆
反之,每个节点都小于其左右节点。
性质
为了维护这种性质,所以需要一个函数去对比相对根节点和起左右节点的大小,然后选出一个最大的放在相对根节点的位置。然后如果相对根节点被换到左或者右节点,这时候又要去检测换完以后的一个小树叉
的性质。所以这需要一个递归或者迭代再去检测新的相对根节点和其左右子节点(也就是之前的根节点被换到的位置)。
在对比相对根节点和其左右子节点之前,需要先判断这个相对根节点是否存在左右子节点,也就是左右节点在数组中的下标是否越界。
在最大二叉堆中,维护这种性质是使小元素下沉的过程(大元素只能上浮一个位置)。
建堆
为了将一个数组建成一个二叉堆,需要事数组中的元素符合上述性质。
所以我么你可以遍历每一个元素,这样她就可以满足了。
维护二叉堆性质的函数是让小元素下沉,而大元素只能上浮一层。所以,如果采用从头到尾遍历的话,那么大元素也只能上浮一层。
而采用从后向前遍历的话,在一层中一个大元素上浮以后,该函数遍历道上一层以后,又会在该节点执行一次。这样一个大元素可以被一直往上“赶”。
插入和删除
如果非要在二叉堆上实现插入和删除的话:
- 删除
删除任意一个点,后面的数据先前进一位,会造成后面大量的树叉
的性质改变。
所以,采用另一种方式,把要删除的元素(下标为k
)和最后一个元素交换,然山删除数组的最后一个元素。
这时候只要调用维护性质的函数,将k
位置的元素下沉就行了。
所以可以这个实现,依次将最大的元素删除或者提出。
- 添加的话(假设使用的是
vector
存储不是数组),那么需要将最后一位(也就是添加的)和第一位交换,然后重新建堆。
优先队列
如果将二叉堆中排序的元素看作是一种结构体,他有一个key
:也就是被排序的值,还有一个value
:也就是该元素自身所带有的值。
那么就可以实现一个优先队列。
key
是权重,也就是要被处理的优先级。而value
可以时要被处理的信息。
每次优先队列(二叉堆)pop
出权重最大的元素,然后被某个函数处理。