堆知识梳理

堆知识梳理

堆的特点

1.堆是一棵完全二叉树,所以除了根节点和最后一个左子结点可以没有兄弟结点,其它结点都必须有
2.根节点中的数要么是堆中的最大数(大根堆),要么是堆中的最小数(小根堆)
大根堆:

小根堆:

堆的存储

我们在前面提到过,堆是一棵完全二叉树,所以它是用数组存储的,根节点存在下标为1的位置,此后每个结点(在n处)的左子节点存在2n处,右子节点存在2n+1处

下标 1 2 3 4 5 6 7
数字 95 38 45 11 20 33 27

手写堆

虽然接下来的内容有点复杂,但还是希望大家自己手打一边,手打堆可以发掘出堆的更多用法

加入数据

首先,我们有一个数据52,然后,我们有一个伟大的决定,将它加入大根堆(小根堆)

下标 1 2 3 4 5 6
数字 84 55 32 26 42 12

然后,我们将新数据放在树(数组)的末尾

下标 1 2 3 4 5 6 7
数字 84 55 32 26 42 12 52

最后,我们将它与它的父亲比较,如果它大于(小于)它的父亲,那么交换,重复这一系列动作,直到它的父亲大于(小于)它为止

下标 1 2 3 4 5 6 7
数字 84 55 52 26 42 12 32

代码

void put(int n){//大根堆代码,n为加入的值
	int now,nex;//now为现在的下标,nex为heap[now]的父亲
	heap[++heap_size] = n;//把n加到数组的最后
	now = heap_size;
	while(now > 1) {
		nex = now / 2;
		if(heap[now] <= heap[nex]) break;
		swap(heap[now],heap[nex]);//交换heap[now]和heap[nex]
		now = nex;
	}
}
void put(int n){//小根堆代码,n为加入的值
	int now,nex;//now为现在的下标,nex为heap[now]的父亲0
	heap[++heap_size] = n;//把n加到数组的最后
	now = heap_size;
	while(now > 1) {
		nex = now / 2;
		if(heap[now] >= heap[nex]) break;
		swap(heap[now],heap[nex]);//交换heap[now]和heap[nex]
		now = nex;
	}
}

弹出数据

堆只支持对堆顶进行弹出,所以弹出后我们还需要继续维护堆
弹出后,我们的堆会变成两个堆,依旧有序

下标 1 2 3 4 5 6 7
数字 55 52 26 42 12 32

我们将这两个堆的堆顶中大的(小的)那个拿来做堆顶

下标 1 2 3 4 5 6 7
数字 55 52 26 42 12 32

我们可以发现,被那去做堆顶的那个数据的所属堆的堆顶不见了(如上面表格下标为2的空位)
那么我们重复做上面的步骤直到移到叶子结点

下标 1 2 3 4 5 6
数字 55 52 42 12 32 26

代码

int pop(){//大根堆代码
	int now = 1,nex,res = heap[1];//now为现在的下标,nex为heap[now]的父亲,res为堆顶
	heap[1] = heap[heap_size --];
	while(now * 2 <= heap_size){
		nex = now * 2;
		if(nex < heap_size && heap[nex + 1] < heap[nex]) nex ++;
		if(heap[now] >= heap[nex]) break;
		swap(heap[now],heap[nex]);
		now = nex; 
	}
	return res;
}
int pop(){//小根堆代码
	int now = 1,nex,res = heap[1];//now为现在的下标,nex为heap[now]的父亲,res为堆顶
	heap[1] = heap[heap_size --];
	while(now * 2 <= heap_size){
		nex = now * 2;
		if(nex < heap_size && heap[nex + 1] < heap[nex]) nex ++;
		if(heap[now] <= heap[nex]) break;
		swap(heap[now],heap[nex]);
		now = nex; 
	}
	return res;
}

STL

STL是c++自带的,操作简便,但过于死板,堆是由优先队列实现的

定义

优先队列的排序顺序不是以到达的先后顺序排序,而是按照其优先级排序,与堆相同,所以用其来实现堆
优先队列属于

#include<queue>

定义

priority_queue <int,vector<int>,greater<int> >q1;//小根堆
priority_queue <int,vector<int>,less<int> >q2;//大根堆
priority_queue <int>q3;//大根堆
注意:q1,q2定义时<int> >q1的空格

拓展:vector为STL中的动态数组

基本操作

q.empty();//如果队列为空,返回1,否则返回0 
q.size();//返回队列中元素的个数 
q.pop();//删除队首元素,无返回值 
q.top();//返回队首元素 
q.push(x);//让x入队 
q.back();//返回队尾元素 
q.emplace();//造一个数据插入队中 (c++11新特性) 

对顶堆

是由一个大根堆和一个小根堆组成的,用于求第k大

思想

我们建一个大根堆,一个小根堆
其中一个限制它大小为k,如果一个数据大于(小于)这个堆的顶,那么存入,否则存入另一个堆
这样最后限制大小的堆的堆顶就是最终答案
我们来看一道题 The kth great number(对顶堆)

完结撒花

欢迎大家留言
小编蒟蒻一个,有什么问题请大佬不惜赐教Orz

posted @ 2023-07-23 21:28  骆美辰  阅读(36)  评论(0编辑  收藏  举报
lock: { enable: true, background: 'https://img1.baidu.com/it/u=2788089125,168843488&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1690563600&t=35fa4326e773b3fbf83562ad746b7cd2',//锁屏背景 strings: [ 'Every win named never give up 每一份胜利都叫不放弃',//签名 ], },