算法竞赛进阶指南0x17 二叉堆

二叉堆是一种数据结构,支持插入、删除和查询最值操作。大根堆和小根堆是两种形式,满足不同权值比较规则。插入操作将新节点放至数组末尾并向上调整,删除操作涉及堆顶元素替换和重新调整。时间复杂度通常为O(logN)。C++的priority_queue提供了部分二叉堆操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        二叉堆是一种支持插入、删除、查询最值的数据结构。它其实是一颗满足"堆性质"的完全二叉树 (完全二叉树:叶子节点都在最后两层,且在最后一层集中于左侧的二叉树),树上的每个节点带有一个权值。若树中的任意一个节点的权值都小于等于其父节点的权值,则称该二叉树满足"大根堆"性质。若树中任意一个节点的权值都大于等于其父节点的权值,则称该二叉树满足"小根堆性质"。满足"大根堆性质"的完全二叉树就是"大根堆",而满足"小根堆性质"的完全二叉树就是"小根堆",二者都是二叉堆的形态之一。

        根据完全二叉树的性质,我们可以采用层次序列存储方式,直接用一个数组保存二叉堆。层次序列存储方式,就是逐层从左到右为树中的节点依次编号,把此编号作为节点在数组中存储的位置(下标)。在这种存储方式中,父节点编号等于子节点编号除以2,左子节点编号等于父节点编号乘2,右子节点编号等于父节点编号乘以2加1。

我们以大根堆为例探讨堆支持的几种常见操作的实现。

$$ Insert $$

        Insert(val) 操作向二叉堆中插入一个带有权值 val 的新节点。我们把这个新节点直接放在存储二叉堆的数组末尾,然后通过交换的方式向上调整,直至满足堆性质。

$$ 时间复杂度为堆的深度,即 O(logN). $$ 

int heap[N], n;

void up(int p) {
	//向上调整
	while (p > 1) {
		if (heap[p] > heap[p >> 1]) {
			// 子节点 > 父节点, 不满足大根堆性质
			std::swap(heap[p], heap[p >> 1]);
			p >>= 1;
		} else {
			break;
		}
	}
}

void Insert(int val) {
	heap[++n] = val;
	up(n);
}

$$ GetTop $$

        GetTop 操作返回二叉堆的堆顶权值,即最大值 heap[1]。 

$$ 时间复杂度为 O(1) $$

int GetTop() {
	return heap[1];
}

$$ Extract $$

         Extract 操作把堆顶从二叉堆中移除。我们把堆顶 heap[1] 与存储在数组末尾的节点 heap[n] 交换,然后移除数组末尾节点(n--),最后把堆顶通过交换的方式向下调整,直至满足堆性质。

$$ 时间复杂度为 O(logN). $$

void down(int s) {
	int p = s << 1;
	while (s <= n) {
		if (s < n && heap[s] < heap[s + 1]) s++;
		// 左右子节点中取较大者
		if (heap[s] > heap[p]) {
			// 子节点大于父节点, 不满足大根堆性质
			std::swap(heap[s], heap[p]);
			p = s;
			s = p << 1;
		} else {
			break;
		}
	}
}

void Extract() {
	heap[1] = heap[n--];
	// heap[1] = heap[n];
	// n--;
	down(1);
}

$$ Remove $$

        Remove(p) 操作把存储在数组下标 p 位置的节点从二叉堆中删除。与 Extract 相类似,我们先把 heap[p] 与 heap[n] 交换,然后令 n 减 1。注意此时 heap[p] 既有可能需要向下调整,也有可能需要向上调整,需要分别进行检查和处理。

$$ 时间复杂度为 O(logN) $$

void Remove(int k) {
	heap[k] = heap[n--];
	up(k), down(k);
}

        C++ STL中的 priority_queue (优先队列) 为实现一个大根堆,支持push(Insert),top(Gettop) 和 pop(Extract) 操作,不支持 Remove 操作,详细用法参见 0x71 节。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值