基本数据结构——二叉堆
迅速补档,为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,我还有事我先走了(逃)。