首先,堆是一种完全二叉树,堆可以分为最小堆和最大堆。最小堆的儿子节点一定不小于它的父亲节点(a[parent]<=a[child],即根节点最小),最大堆的儿子节点一定不大于它的父亲节点(a[parent]>=a[child],根节点最大)。一般,堆排序算法使用的是最大堆,优先队列使用的是最小堆。
如果把堆看成一棵树,一个堆中的节点的高度就是该节点到叶子节点最长简单路径上边的数目。从而,堆的高度即为根节点的高度。如果一个堆含有n个元素,则该堆的高度为floor(log2n),2是底数,floor()向下取整。
堆的插入与删除操作与堆的深度成正比,所以,如果一共n个元素,那么每个操作可以在O(logn)的时间内完成
堆可以用数组来存储,若记父亲节点下标为i,则左儿子节点下标:2*i+1 。右儿子节点下标:2*i+2 。
实现(以最小堆为例):
//heap为存储堆的数组,sz为堆的元素个数
int heap[MAX],sz = 0;
void push(int x){ //x为要插入的元素
int i = sz++; //插到最后,元素个数加一
while(i>0){
int p = (i-1)/2; //与父节点 比较大小
if(heap[p]<=x) //若没有大小颠倒,则退出
break;
heap[i] = heap[p]; //将父节点的值放下来
i = p; //子节点标记提上去
}
heap[i] = x;
}
int pop(){
int ret = heap[0]; //要删除的元素
int x = heap[--sz]; //准备提最后一个元素,且元素个数减一
int i = 0; //插入位置
while(i*2+1<sz){
int LeftChild = i*2+1; //比较要插入的位置的两个儿子节点
int RightChild = i*2+2;
if(RightChild<sz && heap[RightChild]<heap[LeftChild])
LeftChild = RightChild; //总保证左儿子最小
if(heap[LeftChild]>=x) //如果最小的子节点都比自己大,则退出
break;
heap[i] = heap[LeftChild]; //否则将最小的儿子提上来
i = LeftChild;
}
heap[i] = x;
return ret;
}