从数据的
存储结构看,最大堆/最小堆是一个
数组。
从数据的
逻辑结构看,最大堆/最小堆是一棵
完全二叉树。
堆有以下三种基本操作:
1.初始化:将一个无序的序列初始化成堆。从最后一个非叶子结点(namely, (max_index-1)/2)开始,自右向左,自下向上,对每一个根结点执行siftdown操作。
O(N)。
2.插入。在数组的末尾插入新的元素,然后执行siftup操作。
O(logN)。
3.删除。删除指定位置的元素,用数组末尾的元素代替。然后视情况执行siftup或者siftdown操作(注意这两个操作是互斥的,只能执行其中之一)。
O(logN)。
当前元素若可能与下一层元素交换,就是siftdown;若可能与上一层元素交换,就是siftup。
或者说当前元素被“挖出”后形成的“坑”,若往上升就是siftup,若往下降就是siftdown。
以下是siftup和siftdown的代码(以最小堆为例):
void siftup(int position)
{
int child=position;
int father=(child-1)/2;
int temp=minheap[position]; //把要处理的元素“挖出来”,形成一个“坑”
while(father>=0 && child>=1)
{
if(minheap[father]>temp)
{
minheap[child]=minheap[father]; //“坑”往上升
child=father; //更新下标
father=(child-1)/2;
}
else
break;
}
minheap[child]=temp; //把temp填回“坑”里去
}
void siftdown(int position, int heapsize)
{
int father=position;
int child=father*2+1;
int temp=minheap[position]; //仍然是挖坑
while(child<heapsize)
{
if(child<heapsize-1 && minheap[child]>minheap[child+1]) //两个儿子较小的哪一个
child=child+1;
if(temp>minheap[child])
{
minheap[father]=minheap[child]; //坑往下沉
father=child; //更新下标
child=2*father+1;
}
else
break;
}
minheap[father]=temp; //填坑
}