二叉堆的实现

一.堆的简单介绍.

是一种OI中常用的数据结构,通常用于维护一个集合,支持以下几种操作:
1.查询集合中元素的最大(最小)值.
2.给集合加入一个元素 x x x.
3.删除集合中的最大(最小)值.
4.得到集合大小.
5.判断集合是否为空.

根据堆维护的是最大值还是最小值我们可以把堆分为大根堆小根堆两种.

二叉堆是一种最简单好些但也是最朴素的堆,它实现操作2,3是 O ( log ⁡ n ) O(\log n) O(logn)且实现剩下三个操作是 O ( 1 ) O(1) O(1)的.


二.堆性质与二叉堆的定义.

堆性质:称一棵带点权有根树满足堆性质,当且仅当对于它的每一个节点 k k k k k k的点权大于(或小于) k k k每一个儿子的点权.

方便起见,接下来我们的堆均为小根堆,也就是说根为最小值.

二叉堆是一棵满足堆性质的带点权完全二叉树,且节点 k k k的左右儿子分别为 2 k 2k 2k 2 k + 1 2k+1 2k+1.

二叉堆可以只存每一个节点的点权和一个变量表示节点个数,用代码即:

int tr[N+9],cn;



三.二叉堆中的向上调整与向下调整.

因为插入和删除操作,二叉堆中可能会有一些节点的出现或消失导致一些节点破坏堆性质,这个时候就需要向上调整函数 U p ( k ) Up(k) Up(k)和向下调整函数 D o w n ( k ) Down(k) Down(k)了.

向上调整就是说,若节点 k k k的点权小于它父亲的点权,就把 k k k和它父亲的信息交换.

同理,向下调整就是在节点 k k k的点权大于它儿子的点权时,把 k k k与它儿子的信息交换.

值得注意的是,向下调整的时候,若 k k k的两个儿子的点权均小于 k k k,则要取两个儿子中较小的与 k k k交换.

代码如下:

void Up(int k){
  while (k>1&&tr[k]<tr[k>>1])
    swap(tr[k],tr[k>>1]),k>>=1;
}

void Down(int k){
  for (int t=k<<1;t<=cn;k=t,t=t<<1){
    if (t<cn&&tr[t+1]<tr[t]) ++t;
    if (tr[t]<tr[k]) swap(tr[t],tr[k]);
    else break;
  }
}



四.实现基本的五个操作.

操作4,5可以直接通过大小做,操作1只需要返回堆顶即可,接下来主要讨论操作2,3.

操作2是加入一个元素,我们可以把元素直接加到数组最末尾(即下标 c n + 1 cn+1 cn+1),然后直接堆这个元素进行向上调整即可.

操作3是删除堆顶,可以直接把堆顶(即下标 1 1 1)与数组最末尾(即下标 c n + 1 cn+1 cn+1),然后让 c n cn cn减小 1 1 1,并把堆顶向下调整即可.

代码如下:

bool Empty(){return cn==0;}
int Size(){return cn;}
int Top(){return tr[1];}
void Push(int x){tr[++cn]=x;Up(cn);}
void Pop(){swap(tr[cn],tr[1]);--cn;Down(1);}



五.例题与代码.

题目:luogu3378.

代码如下:

#include
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=1000000;

int n,tr[N+9],cn;

void Up(int k){while (k>1&&tr[k]<tr[k>>1])swap(tr[k],tr[k>>1]),k>>=1;}

void Down(int k){
  for (int t=k<<1;t<=cn;k=t,t=t<<1){
    if (t<cn&&tr[t+1]<tr[t]) ++t;
    if (tr[t]<tr[k]) swap(tr[t],tr[k]);
    else break;
  }
}

bool Empty(){return cn==0;}
int Size(){return cn;}
int Top(){return tr[1];}
void Push(int x){tr[++cn]=x;Up(cn);}
void Pop(){swap(tr[cn],tr[1]);--cn;Down(1);}

Abigail getans(){
  scanf("%d",&n);
  int opt,x;
  while (n--){
  	scanf("%d",&opt);
  	switch (opt){
      case 1:
      	scanf("%d",&x);
      	Push(x);
      	break;
      case 2:
      	printf("%d\n",Top());
      	break;
      case 3:
      	Pop();
      	break;
	}
  }
}

int main(){
  getans();
  return 0;
}

你可能感兴趣的:(算法入门)