迅速补档,为A*做一下铺垫…
概念定义
二叉堆就是一个支持插入、删除、查询最值的数据结构。他其实是一棵完全二叉树。那么堆一般分为大根堆和小根堆
大根堆
树中的任意一个节点的权值都小于或者等于其父节点的权值,则称该二叉树满足大根堆性质。
小根堆
树中的任意一个节点的权值都大于或者等于其父节点的权值,则称该二叉树满足小根堆性质。
习惯用法
一般习惯把堆用数组保存。才用父子二倍的编号方式。即:对于某一个节点x,其左儿子节点为2*x,右儿子节点为x*2+1
支持功能及代码实现
Insert插入
向二叉堆中插入一个节点。我们首先把这个节点放在堆的末尾,然后再向上层层递归更新。时间复杂度为O(log N)
int heap[Size],n;
void up(int p){
while(p>1){
if(heap[p]>heap[p/2]){
swap(heap[p],heap[p/2]);
p/=2;
}
else{
break;
}
}
}
void Insert(int val){
heap[++n]=val;
up(n);
}
注:编者在写这篇博文的时候,由于内容过于基础,所以手速大约为20迈。如若有代码错误,敬请指出与纠正。
GetTop取首
返回二叉堆堆顶的值,时间复杂度O(1)
int GetTop(){
return heap[1];
}
Extract去首
操作原理是将堆首取出,然后与堆尾发生交换,然后通过自减操作删除堆尾,再进行一次向下更新。时间复杂度为O(log N)
void Down(int p){
int s=p*2;//p的左儿子
while(s<=n){
if(s<n&&heap[s]<heap[s+1]){
s++;//取左右两儿子中较大者的编号
}
if(heap[s]>heap[p]){
swap(heap[s],heap[p]);
//这里是大根堆,所以如果子节点大于父节点,是不满足性质的。
p=s,s=p*2;
}
else{
break;
}
}
}
void Extract(){
heap[1]=heap[n--];
down(1);
}
Remove删除
这个操作实现把下标为p的节点删除。这玩意和Extract相似,把heap[p]和heap[n]交换,然后n自减。但这时候到底需要向下更新还是向上更新并不好说。所以我们两个都要。
void Remove(int k){
heap[k]=heap[n--];
up(k);
Down(k);
}
STL助你偷懒
写了那么多废话,然而STL里有一个priority_queue(优先队列)实现了一个大根堆。支持push插入,top取首,pop去首。然而没有Remove这种定点删除的操作。考虑到A*算法用的是小根堆,这其实对A*并没什么卵用。保险起见…我去翻了一下小蓝书…发觉……
STL可以实现小根堆,是我在口胡(口头胡扯)……
虽然STL真的只支持大根堆…但是我们可以喂他吃一个魅惑菇啊!!
就是说,我们通过重载“<”运算符,让STL认为大就是小,小即是大!你说的黑不是黑~
那,为了防止我们正常使用"<",我们选择用结构体让他重载。
struct rec{
int id;
double val;
};
bool operator <(const rec &a,const rec &b){
return a.val>b.val;
}
题目中的Show Time
啊只要你想到处都能Show,我还有事我先走了(逃)。