首先,堆是将一个个元素以完全二叉树的分布形式储存起来,因此,在保证每一个元素(x)的左结点(2x)和右结点(2x+1)都比自身小的情况下,就可以实现最基本的排序,由此进行对堆的其他操作。
初始定义值:
const int N=1000010;
int h[N],ph[N],hp[N],size,m;
h储存每个位置的值,ph和hp用来保证两个元素位置交换时自身插入次序的不变,size用来代表当前使用的,下一个将要插入的新值的位置,也就是二叉树的最下层的最右边。可以理解为size记录h的位置,m记录ph、hp的位置。
关键模板:
一:
void heap_swap(int a,int b)
{
swap(ph[hp[a]],ph[hp[b]]);
swap(hp[a],hp[b]);
swap(h[a],h[b]);
}
堆所用到的专属交换函数,通过两组额外数组使得a与b的插入次序最终不变,然后交换数值。
二:
void down(int u)
{
int t=u;
if(u*2<=size&&h[u*2]<h[t]) t=u*2;
if(u*2+1<=size&&h[u*2+1]<h[t]) t=u*2+1;
if(u!=t)
{
heap_swap(u,t);
down(t);
}
}
向下处理,先用t存入u,判断u的左结点是否存在以及是否比t的值小,是则用t存入左结点,再判断右结点是否存在以及比较大小,最后判断t是否与u相等,这样就可以实现u与其左、右结点中较小值交换,然后反复进行“向下操作”,知道u在一且不大于它的值下面。
三:
void up(int u)
{
while(u/2&&h[u/2]>h[u])
{
heap_swap(u/2,u);
u/=2;
}
}
当u的上位结点存在数值比自身大时,与之交换,一直重复至条件不符时。
---------------------------------------------------------------------------------------------------------------------------------堆中常见操作:
一、插入一个数:
cin>>x;
size++;
m++;
ph[m]=size,ph[size]=m;
h[size]=x;
up(size);
正常的赋值与向上操作。
二、求集合中当前最小值:
cout<<h[1];
根据以上操作,作为根结点的第一个值就是最小值。
三、删除最小值:
heap_swap(1,size)
size--;
down(1);
先用最后的值覆盖掉第一个值,再对第一个值进行向下操作。
四、删除第k个值:
heap_swap(k,size);
size--;
down(k),up(k);
与删最小值相似,因为不确定第k个值与末值孰大孰小,所以无脑down+up。
五、修改第k个值:
h[k]=x;
down(k),up(k);
与上同理。
当“第k个值”变为“第k个插入的值”时,只需在前面加上k=ph[k]。
--------------------------------------------------------------------------------------------------------------------------------以上便是我认识的关于堆的知识。