[算法笔记]二叉堆

二叉堆

逻辑上是树(并且是完全二叉树),但是存储上顺序存储(数组)而非链式存储。

既然树和数组能够一一对应上,必然遵从某种关系,经分析易得下列结论:

C h i l d l e f t = P a r e n t ∗ 2 + 1 Child_{left}=Parent * 2 + 1 Childleft=Parent2+1

C h i l d r i g h t = P a r e n t ∗ 2 + 2 Child_{right}=Parent * 2 + 2 Childright=Parent2+2

P a r e n t = ( S o n − 1 ) / 2 Parent = (Son - 1 ) / 2 Parent=(Son1)/2

图1
当然,上述公式前提是从index = 0开始存储的。

如果是从index = 1开始存储的,则是:

int left(int parent){
    return 2 * parent;
}
int right(int parent){
    return 2 * parent + 1;
}
int parent(int son){
	//判断处理无效输入
    if (son <= 0){
    	return -1;
	}
    return (son - 1) / 2;
}

index = 1开始存储最直观的好处就是树的第i层第一个元素下标index = 2^(i-1)


然后增删改查有个共同点:围绕两个操作进行。

也就是向上调整和向下调整:

  • 向上调整:如果这个结点的权值大于它父亲的权值,就交换,重复此过程直到不满足或者到根。

  • 向下调整:在该结点的儿子中,找一个最大的,与该结点交换,重复此过程直到底层。

void up(int x) {
	while (x > 1 && heap[x] > heap[x / 2]) {
		swap(heap[x], heap[x / 2]);
		x /= 2;
	}
}
void down(int x) {
	while (x * 2 <= n) {
		int t = x * 2;
		if (t + 1 <= n && heap[t + 1] > heap[t]) t++;
		if (heap[t] <= heap[x]) break;
		std::swap(heap[x], heap[t]);
		x = t;
	}
}

向上调整结束条件之一是调整元素下标x > 1,因为根节点i = 1不用被调整。另一个条件是该节点大于其父节点,满足这俩就将该节点和其父节点交换(显然这是大根堆)

向下调整就略显繁琐一点。先需要取左子节点(用t保存其下标),然后再看看旁边的右子节点存在与否,存在的话是不是比它大。选择最大的那个子节点,跟父节点比较,符合交换条件的话(即子节点比父节点大)交换。

插入与删除操作

插入在堆底,删除在堆顶

插入操作就是在最下一层最右边的叶子之后插入,或者说是数组最后一个存有内容的单元后面那个地方插入

插入完未必符合二叉堆的性质,再向上调整就是了。

时间复杂度 O ( l o g n ) O(logn) O(logn)


删除就是删除根节点,或者说是heap[0]。一般是把根结点和最后一个结点直接交换。然后删除最后一个节点,然后对新的根节点向下调整。

时间复杂度 O ( l o g n ) O(logn) O(logn)

void insertHeap(int key){
	n = n + 1;
	heap[n] = key;
	up(n);
}
void deleteHeap(){
	// 删除树根 
	swap(heap[1], heap[n]);
	n = n - 1;
	down(1);
}

实现

完整代码如下

#include<iostream>
using namespace std;

int heap[2048];
int n = 0;
// 获取父节点或者左右子节点的操作
int left(int parent){
    return 2 * parent;
}
int right(int parent){
    return 2 * parent + 1;
}
int parent(int son){
    if (son <= 0){
    	return -1;
	}
    return (son - 1) / 2;
}
// 上下调整的操作
void up(int x) {
	while (x > 1 && heap[x] > heap[x / 2]) {
		swap(heap[x], heap[x / 2]);
		x /= 2;
	}
}
void down(int x) {
	while (x * 2 <= n) {
		int t = x * 2;
		if (t + 1 <= n && heap[t + 1] > heap[t]) t++;
		if (heap[t] <= heap[x]) break;
		std::swap(heap[x], heap[t]);
		x = t;
	}
}
// 堆插入与删除的操作
void insertHeap(int key){
	// 插入处始终是尾部
	n = n + 1;
	heap[n] = key;
	up(n);
}
void deleteHeap(){
	// 删除的始终是树根节点
	swap(heap[1], heap[n]);
	n = n - 1;
	down(1);
}
// 打印整个堆的内容
void printHeap(){
	for(int i = 1; i <= n ; ++i){
		cout<<heap[i]<<" ";
	}
	cout<<endl;
}

参考

https://oi-wiki.org/ds/binary-heap/

https://blog.csdn.net/qq_35862309/article/details/108455062

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值