二叉堆,是一种实现优先队列的数据结构.
二叉堆是一棵满足堆性质的完全二叉树,堆性质就是一棵树内所有父节点的键值都大于或小于子节点的键值(至于是大于还是小于就看是大根堆还是小根堆了).
至于如何实现,就比较烦了.(想想你当年刚开始学线段树有多艰难现在学堆就有多艰难)
首先,堆一开始肯定是空的.
且父节点的编号一定是子节点编号除2加1,左儿子编号一定是父节点编号乘2,右儿子编号一定是父节点编号乘2加1.
那么二叉堆的结构体如何定义呢?
每个节点只需要维护键值就可以了.
所以连结构体都不用.
堆维护的东西如下:
const int N=1000000; int tr[5*N],top=0;
那么接下来讨论小根堆.
首先小根堆建树很简单,啥都不用做.
接下来是判空:
bool empty(){ if (top_node) return false; else return true; }
接下来是查找最小值的操作,直接弹出堆顶:
int top(){ return tr[1]; }
接下来是插入某个值,形式为push(x).
这个操作二叉堆是从堆的尾部先加入一个值,之后不断地向上调整,直到稳定下来.
就像这样一棵树往上调:
过程如下:
那么就是不断地往上调,直到当前值大于父节点的值.
代码如下:
void push(int x){ tr[++top_node]=x; int k=top_node; while ((k>>1)&&(tr[k]<tr[k>>1])) swap(tr[k],tr[k>>1]),k>>=1; }
删除就是将最后一个结点的值将根节点覆盖,之后不断地往下调整,且top_node--.
主要就是像这样一个过程:
具体代码可以这样实现:
void pop(){ tr[1]=tr[top_node--]; int k=1,next; while (1){ next=k<<1; if (next>top_node) break; if (next+1<=top_node&&tr[next]>tr[next+1]) next++; if (tr[next]<tr[k]) swap(tr[k],tr[next]),k=next; else break; } }
注意一定当有两个子节点可供你选择的时候一定要选择更小的那个来换,不然就会破坏堆的性质.
那么整一个二叉堆如下:
const int N=1000000; int tr[N+1],top_node=0; bool empty(){ if (top_node) return false; else return true; } int top(){ return tr[1]; } void push(int x){ tr[++top_node]=x; int k=top_node; while (k>1&&(tr[k]<tr[k>>1])) swap(tr[k],tr[k>>1]),k>>=1; } void pop(){ tr[1]=tr[top_node--]; int k=1,next; while (1){ next=k<<1; if (next>top_node) break; if (next+1<=top_node&&tr[next]>tr[next+1]) next++; if (tr[next]<tr[k]) swap(tr[k],tr[next]),k=next; else break; } }