堆(优先队列)的基本操作
堆是一棵完全二叉树,所以可以利用数组来实现。
以1号作为root,则对于数组中的任意一个i,其左儿子在(2*i),右儿子在(2*i+1),父亲在(i/2)。
下面以最小堆为例,实现一些基本操作:
1.堆的声明
#define INF 0x3f3f3f3f
#define MAX 1005
int heap[MAX];
heap[0] = -INF;
int Size = 0;
2.两个基本的操作:PercolateUp,PercolateDown
//比较父节点与子节点,若父节点大,则交换,实现子节点的上滤
void PercolateUp(int p){
while(p/2){
if(heap[p] < heap[p/2]){
swap(heap[p], heap[p/2]);
p /= 2; //更新p
}
else break;
}
}
//将左右两个儿子节点与父节点比较,若父节点大,下滤(与儿子节点交换)
void PercolateDown(int p){
int len = Size;
while(p*2 <= len){
if(heap[p*2] < heap[p]){ //左儿子
swap(heap[p], heap[p*2]);
p *= 2;
}
else if(heap[p*2 + 1] < heap[p]){ //右儿子
swap(heap[p], heap[p*2 + 1]);
p = (p*2)+1;
}
else return;
}
}
3.Insert
将元素先插入到末尾的下一个位置(保证为完全树),然后逐层上滤,寻找正确的位置。
每一次上滤都需要反复交换,但是纵观整个过程,会发现每一步只需要将不满足条件的父亲下移,当找到正确的位置之后,再进行一步交换即可。
如果插入的元素是整个堆的最小值,则X会被一直推到堆顶。此时X大于heap[0](-INF),循环终止,交换。
void Insert(int x){
if(Size == MAX - 1){
cout<<"FULL"<<endl;
return;
}
int i;
//将x置于最后,然后向上调整(与他爹比较)
for(i = ++Size; data[i / 2] > x; i/=2)
data[i] = data[i/2];
data[i] = x;
}
4.DeleteMin
//删除堆顶的最小元,之后将最后一个节点放到1号位置,然后PercolateDown,进行调整,保持完全树
int DeleteMin(){ int i, child; if(Size == 0){ cout<<"Empty"<<endl; return ERROR; } int MinElem = heap[1]; int LastElem = heap[Size--]; for(i = 1; i*2 < Size; i = child){ child = i*2; //找相对较小的儿子节点 if(child != Size && heap[child + 1] < heap[child]) child++; //percolate one level if(Last > heap[child]) heap[i] = heap[child]; else break; } heap[i] = LastElem; return MinElem; }