堆是完全二叉树的一种。

所谓完全二叉树,就是每一个有孩子的节点一定有左孩子的二叉树

举几个例子

    

 上面都是完全二叉树,最后一个是满二叉树,也是完全二叉树

 而下面的就不是

完全二叉树的结构方便存储。若我们设一个节点的编号为i,那么不难发现它的左孩子是2i,右孩子是2i+1,它的父亲是i/2(整除),这方便我们用一维数组模拟。

小根堆:

对于任何一个节点i来说,a[i/2]<=a[i]且a[i]<=a[2*i],a[i]<=a[2*i+1](如果存在左右孩子的话)

即每个节点的值必定大于等于其父节点,小于等于其子节点。

大根堆:

将小根堆反过来即为大根堆。

不难看出,堆是要动态维护的。所以我们如何将一个数加入到堆里/从堆里取出呢?

加入(这里是小根堆):

 1.  在堆尾先加入一个元素,并将该节点置为当前节点i。

  2. 将i与i/2比较,如果小于,就交换,并将当前节点编号改为i,继续如此比较。如果大于,就结束。(我们称之为上浮)

int cnt=0;
void put(int x)
{  a[++cnt]=x;
   int m=cnt;
   while(m>=1)
   {int next=m/2;
     if(a[m]>=a[next])break;//如果满足小根堆,就停止循环 
     swap(a[m],a[next]);
     m=next;
   }
}

大根堆的加入就把>=改为<=即可

取出:

  1.将根节点取出,并将a[cnt]放到根节点的位置上。

  2.比较该节点的两个儿子(特判没有儿子的情况),将该节点与较小的儿子比较,如果大于,就交换,继续如此比较。如果小于,就停止。

int get()
{int m,next,r;
  r=a[1];
  a[1]=a[cnt--];
  while(m*2<=cnt)
  {next=m*2;
    if(a[next]>a[next+1]&&next+1<=cnt)next++;//如果右儿子比左儿子小并且右儿子存在,
//就比较当前点和右儿子
if(a[m]<a[next])break; swap(a[m],a[next]); m=next; }
return r;//这里是取出,上面是维护堆 }

大根堆依旧是将比较符号改过来。

应用:堆排序。

例题:合并果子瑞瑞的木板快速排序

 

posted @ 2019-04-26 21:33  千载煜  阅读(256)  评论(0编辑  收藏  举报