堆的性质

堆可以认为是一种条件优先的队列,相较于普通的队列,堆每次出队时的元素为整个队列中满足某种极端条件的元素,如最大值(大顶堆),最小值(小顶堆)。

堆的存储结构为一棵完全二叉树,其满足这样一个性质。对于每个节点,其左右子树上的元素均小于(大于)该节点上的元素。这样就可以保证根节点上的元素为整棵树中的元素最小值(最大值)。由于堆使用完全二叉树存储,因此可以用数组来进行堆的实现。

对于堆中的每一个节点,最基本的操作只有两个
-up,即向上调整操作
-down,即向下调整操作
每次up或者down操作的最坏时间复杂度都为O(logn),即取决于二叉树的层数

方便起见,我们让二叉树下标从1开始,这样的话,下标为i的节点的左孩子为2i,右孩子为2i+1,其父节点为i/2
下面以小顶堆为例给出代码实现。其中参数为堆节点的下标

void up(int u)
{
	while (u / 2/*非根节点*/ && h[u] < h[u / 2]) //若父节点比该节点大,说明该节点应上移,即与父节点交换
	{
		swap(h[u], h[u / 2]);
		u >>= 1; // u/2,更新当前节点到其父节点
	}
}
void down(int u)
{
	int t = u; //t存储该节点及其左右孩子节点中最小值的下标
	if(u * 2 <= len && h[u * 2] < h[t]) t = u * 2;
	if (u * 2 + 1 <= len && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
	if (u != t) //若最小并非该节点上的值,交换该节点与其孩子节点的值,并继续向下调整交换后的值,直到不需调整
	{
		swap(h[u], h[t]);
		down(t);
	}
}

堆的插入以及删除操作如下

void insert(int x)
{
	h[++len] = x;//插入到堆的最后
	up(len);//从该最后节点向上调整
}
int pop()
{
	int item = h[1];//保存堆顶元素
	h[1] = h[len--];//将堆中的末尾元素搬到根节点上,相当于删除了根节点
	down(1);//从根节点开始向下调整
	return item;
}

最后给出一个在O(n)时间复杂度内将给定数组建成堆的方案。(若一个一个插入建堆,时间复杂度为O(nlogn))

void build_heap(int len)
{
	for (int i = len / 2; i ; i--)//从二叉树的第二层开始逐个节点向下调整
	{
		down(i);
	}
}

最后给出堆的STL实现,类名为priority_queue(优先队列类),需要包含头文件queue

#include <queue>
int main()
{
	int x;
	priority_queue<int> h;//建立一个空堆,默认为大根堆
	h.push(x);//向堆中插入一个数
	h.top();//返回堆顶的数但不删除
	h.pop();//删除堆顶的数,无返回值
	h.size();//返回堆的规模
	return 0;
}

若想使用小根堆存储数据x,可以使用大根堆存储-x,或者使用下述方法构造小根堆

priority_queue<int,vector<int>,greater<int>> h
posted @ 2023-12-13 22:46  凪风sama  阅读(13)  评论(0编辑  收藏  举报
为博客底部添加音乐组件