浅析二叉堆

原理

堆是一颗完全二叉树,其中堆顶一定是“最大”或“最小”的,这里的“最大”“最小”是相对于优先级而言,并不是简单的数值大小。堆一般分为小根堆和大根堆,即对于任一非根节点,它的优先级都大于或小于堆顶。
其中,C++的STL中的优先队列就是二叉堆的应用。

堆的操作

1.插入

对于堆的插入,实现原理就是不断与其父结点进行比较,如果不满足大根堆或小根堆的性质,则进行交换,由于堆是完全二叉树,所以满足关系式——父结点下标 = 左子树下标 ÷ 2 = (右子树下标 - 1) ÷ 2,左子树下标 = 父结点下标 × 2,右子树下标 = 父结点下标 × 2 + 1。

2.删除

对于堆的删除,实现原理是将堆顶与堆底进行交换,然后交换后的堆顶不断与其子节点进行比较交换,直到重新符合堆的性质。

//以小根堆为例
void push(int x)
{
	heap[++siz] = x;		//siz表示堆的大小
	int cur = siz;
	while (cur)		//还未到根节点,继续比较看是否需要交换
	{
		int nextPos = (cur >> 1);
		if (heap[nextPos] > heap[cur])
		{
			Swap(heap[nextPos, heap[cur]);
		}
		else
		{
			break;
		}
		cur = nextPos;
	}
}
void pop()
{
	Swap(heap[siz], heap[1]);		//交换堆底和堆顶,弹出堆底
	siz--;
	int cur = 1;
	while ((cur << 1) <= siz)		//重新调整堆
	{
		int nextPos = (cur << 1);
		if (nextPos + 1 <= siz && heap[nextPos + 1] < heap[nextPos])
		{
			nextPos++;
		}
		if (heap[nextPos] < heap[cur])
		{
			Swap(heap[nextPos], heap[cur]);
		}
		else
		{
			break;
		}
		cur = nextPos;
	}
}

两个操作的时间复杂度都为 O ( l o g 2   n ) O(log_2\ n) O(log2 n)

STL中的priority_queue

C++的标准模板库中提供了许多实用的函数和数据结构,其中优先队列就利用了二叉堆的结构特点,使用方式如下:

#include<iostream>
using namespace std;
#include<queue>
priority_queue<Type> q;		//也可写作priority_queue<Type, vector<Type>, less<Type> > q,表示大根堆
priority_queue<Type, vector<Type>, greater<Type> > q;		//表示小根堆
//注意greater<Type>和结尾的>中间的空格,某些编译器会将其编译成位运算符>>

优先队列封装的常用函数有如下几个:

q.top();		//取堆顶,但不弹出
q.pop();		//弹出堆顶
q.push(x);		//向堆中插入一个元素
q.empty();		//判断堆是否为空,空返回1非空返回0
q.size();		//返回堆的大小

而一般在使用优先队列时,往往对象并非是一个简单的数据类型。例如将结构体作为对象时,在建堆过程中,此时前言提到的优先级问题就有了重要的意义,往往结构体中变量是不唯一的,那么在建堆时如何根据所需来建一个合理的堆呢,此时就需要重载运算符:

struct node
{
	int val, rnd;
	bool operator < (const node& x)const
	{
		return rnd < x.rnd;
	}
}a[maxn];

通过重载运算符 < 使其表示为rnd的优先级,如此一来就能够完成堆的一系列操作。

参考博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值