- 定义
普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出的行为特征。通常采用堆数据结构来实现。
堆是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵[完全二叉树]的数组对象。堆中某个节点的值总是不大于或不小于其父节点的值。如果父节点的值大于子节点的值,则为最大堆,否则为最小堆。
注意:最大(小)堆并不是高层次的节点的值一定大(小)于低层次的节点的值,而是只保证父节点的值大(小)于等于子节点的值。
在堆的定义中,我们知道堆是一种完全二叉树,当然我们可以使用上一章的方式来完成堆的定义,但是我们可以使用一种更简单的方式——数组。
- 基本使用(如果不提示,则默认为最大堆)
(1)Sift Up(上浮)
当我们想要向堆中添加元素时,必须保证满足堆的性质,也就是某个节点的值总是不大于其父节点的值,所以堆的上浮操作,就是将 "大"值上浮到堆顶,具体过程是当向数组的尾部添加一个新节点之后,判断对应的值如果大于父节点的值,则进行交换,然后在判断父节点和 "爷" 节点,直到堆的根。因为添加新节点之前数据已经满足堆的性质,如果添加元素之后出现子节点的值大于父节点的情况,只能出现在新节点的祖先中。
// 进行堆的上浮操作 sift Up
public void siftUp(int index){
//如果父节点小于子节点的值,则进行交换
while(index > 0 && arr[getParentIndex(index)].compareTo(arr[index]) < 0){
swap(index, getParentIndex(index));
index = getParentIndex(index);
}
}
(2)Sift Down(下沉)
当我们从堆中去除元素之后,会形成两个子树,如果使用融合形成一个新的堆,比较复杂,所以我们使用一种更简便的方式,就是将堆中的最后一个节点替换到头结点,然后在进行下沉操作。从头结点开始进行下沉,如果子节点的值大于当前节点,则将当前节点的值和子节点中的较大值进行替换,直到堆的底部。
//堆的下沉操作 sift down
public void siftDown(int index){
while(getLeftChild(index) < size){
int swapIndex = getLeftChild(index);
if(swapIndex + 1 < size && arr[swapIndex + 1].compareTo(arr[swapIndex]) > 0) swapIndex ++;
if(arr[index].compareTo(arr[swapIndex]) > 0) return;
swap(index, swapIndex);
index = swapIndex;
}
}
(3)replace (替换)
堆的替换操作就是取出堆顶元素,然后替换为新的元素,在进行下沉操作。
(4)heapify (堆化)
堆化就是将任意的数组转化成堆,我们可以使用暴力方式遍历数组然后依次插入节点形成堆,堆化是一种更简单的方式,具体过程就是从堆的最后一个非叶子节点开始往前遍历进行下沉操作。
//堆化
public void heapfiy(E[] arr){
this.arr = arr;
size = arr.length;
for (int i = getParentIndex(arr.length - 1); i >= 0 ; i--) {
siftDown(i);
}
}
- 优先队列的实现(略)
- 堆的扩展
更复杂的堆:d 叉堆、索引堆、二项堆、斐波那契堆