【总结】2020暑假集训--二叉堆

一、概念

二叉堆是一种数组对象,它可以被视为一棵完全二叉树。树中每个结点与数组中存放该结点中值的那个元素相对应,如图。【总结】2020暑假集训--二叉堆_第1张图片
【总结】2020暑假集训--二叉堆_第2张图片

二、性质

设数组 A A A的长度为 l e n len len,二叉树的结点个数为 s i z e size size s i z e ≤ l e n size≤len sizelen,则 A [ i ] A[i] A[i]存储二叉树中编号为 i i i的结点值( 1 ≤ i ≤ s i z e 1≤i≤size 1isize),而 A [ s i z e ] A[size] A[size]以后的元素并不属于相应的堆,树的根为 A [ 1 ] A[1] A[1],并且利用完全二叉树的性质,我们很容易求第 i i i个结点的父结点、左孩子、右孩子的下标,分别是 i / 2 i/2 i/2 2 i 2i 2i 2 i + 1 2i+1 2i+1。(自己看图理解)
【总结】2020暑假集训--二叉堆_第3张图片
大根堆具有这样一个性质:对除根以外的每个结点i,A[fa(i)]≥A[i]。即除根结点以外,所有结点的值都不得超过其父结点的值,这样就推出,堆中最大元素存放在根结点中,且每一结点的子树中的结点值都小于等于该结点的值,这种二叉堆又称为“大根堆”;
反之,称为“小根堆”。

三、堆的操作

插入

假如这是原始的堆
【总结】2020暑假集训--二叉堆_第4张图片
现在我们要插入一个13的节点,首先将他接在二叉树的后面,即7的右儿子【总结】2020暑假集训--二叉堆_第5张图片
可以发现他与他的父节点不满足大根堆的关系,所以交换他们
【总结】2020暑假集训--二叉堆_第6张图片
发现现在它和它的父节点仍然满足大根堆的性质,所以插入完成【总结】2020暑假集训--二叉堆_第7张图片
经过验证,新二叉堆依然满足性质,证明插入没问题

转化为文字语言是:

  1. 在堆尾加入一个元素,并把这个结点置为当前结点。
  2. 比较当前结点和它父结点的大小
    如果如果当前结点大于父结点,则交换它们的值,并把父结点置为当前结点,继续转2。
    如果当前结点小于等于父结点,则转3。
  3. 结束。

代码实现

接下来是代码实现:

1.在堆尾加入一个元素,并把这个结点置为当前结点。

heap[++len]=a;

其中len为堆的元素个数

  1. 比较当前结点和它父结点的大小
    如果如果当前结点大于父结点,则交换它们的值,并把父结点置为当前结点,继续转2。
    如果当前结点小于等于父结点,则转3。
while(now>1){
	int fa=now>>1;
	if(heap[fa]<heap[now]){
		swap(heap[fa],heap[now]);
		now=fa;
	}
	else{
		break;
	}
}

完整代码:

void Put(int a){ //加入节点 
	heap[++len]=a;
	int now=len;
	while(now>1){
		int fa=now>>1;
		if(heap[fa]<heap[now]){ 
			swap(heap[fa],heap[now]);
			now=fa;
		}
		else{
			break;
		}
	}
}

这是大根堆的实现,小根堆就请大家自己通过理解改一下了~

删除

二叉堆主要支持的是删除根节点

依然是来几张图理解一下

这是原始的堆

【总结】2020暑假集训--二叉堆_第8张图片
现在我们删去堆顶,也就是16

【总结】2020暑假集训--二叉堆_第9张图片
但是这样就变成两个堆了,怎么办呢?我们把最后一个节点,也就是13置为堆顶

【总结】2020暑假集训--二叉堆_第10张图片

现在让13像插入时的那样,只是向下变为向上,一一比较儿子节点两(或一)个儿子中值较大的与当前交换

【总结】2020暑假集训--二叉堆_第11张图片

再看看这个堆,是不是就正确了呀

(现在说的和代码都为小根堆实现(毕竟插入用的大根堆,本着公平的原则,用用小根堆))
1、根据需要取出堆中根结点的值。
2、把堆的最后一个结点(heap_size)放到根的位置上,把根覆盖掉, 堆长度减一。

heap[1]=heap[len];
len--;

3、把根结点置为当前父结点,即当前操作结点now。

int now=1;

4、如果now无儿子(now>heap_size/2),则转6;否则,把now的两(或一)个儿子中值较小的那一个置为当前子结点son。

while(now<=len/2){
	int son;
	if(now*2<len && heap[now*2]>heap[now*2+1]){
		son=now*2+1;
	}
	else{
		son=now*2;
	}

5、比较now与son的值,如果now的值小于等于son,转6;否则交换 两个结点的值,把now指向son,转4。

	if(heap[now]<heap[son]){
		break;
	}
	else{
		swap(heap[now],heap[son]);
		now=son;
	}
}

6、结束。

代码实现:

void Get(){
	heap[1]=heap[len];
	len--;
	int now=1;
	while(now<=len/2){
		int son;
		if(now*2<len && heap[now*2]>heap[now*2+1]){
			son=now*2+1;
		}
		else{
			son=now*2;
		}
		if(heap[now]<heap[son]){
			break;
		}
		else{
			swap(heap[now],heap[son]);
			now=son;
		}
	}
} 

四、总结

堆是一种时间复杂度为 l o g log log级的数据结构,主要用来维护一个序列的最值以及删除最值、插入元素并维护最值,堆在NOIP竞赛中应用广泛,常用与快速查询最大(最小值),优化各种算法(如:最短路算法、DP算法),是一种效率高,应用广泛的数据结构。

你可能感兴趣的:(二叉堆-优先队列,总结,算法,数据结构)