一、大顶堆的定义
大顶堆是堆 的一种具体实现,属于完全二叉树结构,同时满足大顶堆的核心性质 :每个父节点的值都大于或等于其左右子节点的值,堆顶(根节点)为整个堆的最大值。
大顶堆常被用于快速获取和维护数据集的最大值,是优先队列、Top-K 大问题等场景的常用底层结构。
资料:https://pan.quark.cn/s/43d906ddfa1b、https://pan.quark.cn/s/90ad8fba8347、https://pan.quark.cn/s/d9d72152d3cf
二、大顶堆的核心性质
1. 结构性质
- 大顶堆是一棵完全二叉树,除最后一层外,其余层节点数均为最大值,且最后一层节点靠左排列。
- 通常用数组存储,若数组索引从 0 开始,对于索引为
i的节点:- 左子节点索引为
2i + 1 - 右子节点索引为
2i + 2 - 父节点索引为
(i - 1) // 2
- 左子节点索引为
2. 数值性质
- 任意父节点的值 ≥ 其左右子节点的值,堆顶(数组索引 0 位置)始终是堆中最大值。
- 叶子节点无需满足额外的数值约束(无后代节点)。
三、大顶堆的核心操作
大顶堆的操作围绕维持堆的数值性质 展开,核心为上浮 和下沉,具体操作如下:
1. 上浮(Up-Heapify)
当插入新元素时,新元素初始放在数组末尾,若其值大于父节点,需通过上浮调整位置。
- 操作逻辑:比较当前节点与父节点的值,若当前节点值更大,则交换两者,重复此过程直到当前节点值 ≤ 父节点,或到达堆顶。
2. 下沉(Down-Heapify)
当堆顶元素被删除或堆中元素值被修改时,需通过下沉调整堆结构。
- 操作逻辑:比较当前节点与左右子节点的值,选择值最大的子节点;若当前节点值小于该子节点,则交换两者,重复此过程直到当前节点值 ≥ 所有子节点,或成为叶子节点。
3. 插入元素
- 将新元素添加到数组末尾;
- 对新元素执行上浮操作,恢复大顶堆性质。
4. 删除堆顶元素
- 将堆顶元素与数组末尾元素交换;
- 删除数组末尾的原堆顶元素;
- 对新的堆顶元素执行下沉操作,恢复大顶堆性质。
5. 构建大顶堆
将无序数组转换为大顶堆,从最后一个非叶子节点 开始(索引为 (n//2)-1,n 为数组长度),从下到上依次执行下沉操作。
四、大顶堆的时间复杂度
- 插入操作:
O(log n),上浮操作最多遍历堆的高度(log n层)。 - 删除堆顶操作:
O(log n),下沉操作最多遍历堆的高度。 - 构建堆:
O(n),优于逐个插入的O(n log n)。 - 获取堆顶最大值:
O(1),直接访问数组索引 0 位置。
五、大顶堆的实现示例
python
class MaxHeap:
def __init__(self):
self.heap = []
def parent(self, idx):
"""获取父节点索引"""
return (idx - 1) // 2
def left_child(self, idx):
"""获取左子节点索引"""
return 2 * idx + 1
def right_child(self, idx):
"""获取右子节点索引"""
return 2 * idx + 2
def swap(self, i, j):
"""交换堆中两个位置的元素"""
self.heap[i], self.heap[j] = self.heap[j], self.heap[i]
def up_heapify(self, idx):
"""上浮操作,维护大顶堆性质"""
while idx > 0 and self.heap[idx] > self.heap[self.parent(idx)]:
self.swap(idx, self.parent(idx))
idx = self.parent(idx)
def down_heapify(self, idx):
"""下沉操作,维护大顶堆性质"""
n = len(self.heap)
while True:
largest = idx # 初始假设当前节点最大
left = self.left_child(idx)
right = self.right_child(idx)
# 比较左子节点
if left < n and self.heap[left] > self.heap[largest]:
largest = left
# 比较右子节点
if right < n and self.heap[right] > self.heap[largest]:
largest = right
# 若当前节点已是最大,停止下沉
if largest == idx:
break
self.swap(idx, largest)
idx = largest
def insert(self, val):
"""插入元素"""
self.heap.append(val)
self.up_heapify(len(self.heap) - 1)
def extract_max(self):
"""删除并返回堆顶最大值"""
if not self.heap:
return None
if len(self.heap) == 1:
return self.heap.pop()
max_val = self.heap[0]
# 堆顶与末尾元素交换,删除原堆顶
self.heap[0] = self.heap.pop()
# 新堆顶下沉
self.down_heapify(0)
return max_val
def get_max(self):
"""获取堆顶最大值(不删除)"""
return self.heap[0] if self.heap else None
def build_heap(self, arr):
"""将无序数组构建为大顶堆"""
self.heap = arr.copy()
n = len(self.heap)
# 从最后一个非叶子节点开始下沉
for i in range((n // 2) - 1, -1, -1):
self.down_heapify(i)
使用示例
python
# 初始化大顶堆
max_heap = MaxHeap()
# 插入元素
max_heap.insert(5)
max_heap.insert(3)
max_heap.insert(8)
max_heap.insert(1)
print("堆顶最大值:", max_heap.get_max()) # 输出 8
# 提取堆顶
print("提取的最大值:", max_heap.extract_max()) # 输出 8
print("当前堆顶:", max_heap.get_max()) # 输出 5
# 用无序数组构建大顶堆
arr = [9, 4, 7, 1, 3, 6]
max_heap.build_heap(arr)
print("构建后的堆顶:", max_heap.get_max()) # 输出 9
六、大顶堆的典型应用
- 优先队列(最大值优先):用于任务调度系统,优先执行优先级高(数值大)的任务。
- Top-K 小问题:维护一个大小为 K 的大顶堆,遍历数据时,若新元素小于堆顶则替换堆顶并下沉,最终堆中元素即为前 K 小值。
- 堆排序:先构建大顶堆,再逐次提取堆顶最大值,将其放到数组末尾,最终得到升序数组。
- 数据流最大值维护:实时数据流中,可通过大顶堆快速获取当前所有数据的最大值。