别再用暴力排序了!大小顶堆让「取极值」效率飙升至 O (log n)

大小顶堆:原理、实现与应用

堆(Heap)是一种基于完全二叉树的数据结构,核心特性是 "父节点与子节点的优先级关系固定"。根据优先级规则的不同,可分为大顶堆小顶堆,二者是实现 "Top K 问题""优先级队列" 等场景的核心工具。

一、核心定义:大顶堆 vs 小顶堆

堆的本质是 "完全二叉树"(除最后一层外,每一层节点数均满;最后一层节点从左到右排列),但需满足以下优先级规则:

类型 核心规则(父节点 vs 子节点) 关键特性
大顶堆 父节点的值 大于等于 其左右子节点的值 根节点是整个堆的 最大值
小顶堆 父节点的值 小于等于 其左右子节点的值 根节点是整个堆的 最小值

示例(数组存储堆)

完全二叉树可通过数组高效存储(无需指针,通过索引计算父子关系),假设数组索引从 0 开始:

  • 父节点索引 i 的左子节点:2*i + 1
  • 父节点索引 i 的右子节点:2*i + 2
  • 子节点索引 j 的父节点:(j - 1) // 2

以数组 [5,3,4,1,2] 为例:

  • 对应大顶堆的完全二叉树:根为 5,左子树 3(子节点 1、2),右子树 4(无后代),满足 "父≥子"。
  • 若数组为 [1,2,4,5,3],则对应小顶堆,根为 1(最小值),满足 "父≤子"。

二、堆的核心操作(以大顶堆为例)

堆的关键操作需保证 "操作后仍满足堆的优先级规则",核心包括插入删除堆顶 ,时间复杂度均为 O(log n) (n 为堆的大小,操作需沿树的高度调整)。

1. 插入操作(Heap Insert)

新元素先加入堆的末尾(数组最后一位),再通过 "向上调整(Heapify Up)" 恢复堆结构:

  1. 将新元素放到数组末尾(完全二叉树的最后一个位置)。
  1. 比较该元素与父节点:若为大顶堆且新元素 > 父节点,则交换二者。
  1. 重复步骤 2,直到该元素≤父节点(或成为根节点),堆结构恢复。

2. 删除堆顶操作(Extract Max/Min)

堆顶(根节点,最大 / 最小值)删除后,需用末尾元素补位,再通过 "向下调整(Heapify Down)" 恢复堆结构:

  1. 取出堆顶元素(根节点),将数组末尾元素移到根节点位置。
  1. 比较该元素与左右子节点:若为大顶堆,找出左右子节点中的最大值,若该元素 < 最大值,则交换二者。
  1. 重复步骤 2,直到该元素≥所有子节点(或成为叶子节点),堆结构恢复。

三、经典应用场景

大小顶堆的核心价值是 "快速获取极值",因此广泛用于需优先处理 "最大 / 最小" 元素的场景:

场景 堆的选择 核心逻辑
Top K 问题 小顶堆(求 Top K 大)大顶堆(求 Top K 小) 求 "前 K 个最大元素":用容量为 K 的小顶堆,遍历元素时,比堆顶大则替换堆顶,最终堆内即 Top K。
优先级队列 大顶堆 / 小顶堆 按优先级处理任务:如 "最高优先级任务先执行" 用大顶堆(优先级为键),"时间最早任务先执行" 用小顶堆(时间为键)。
中位数查找 大顶堆 + 小顶堆 大顶堆存 "左半部分较小元素"(堆顶为左半最大,即中位数候选),小顶堆存 "右半部分较大元素",动态维持两堆大小平衡。
堆排序 大顶堆 1. 将数组构建为大顶堆;2. 反复删除堆顶(放到数组末尾)并调整堆,最终数组升序排列。

四、Python 实现(以小顶堆为例)

Python 标准库 heapq 仅提供小顶堆的实现,若需大顶堆,可通过 "存入负数" 间接实现(取元素时再转负)。

1. 小顶堆基础操作(heapq)

ini 复制代码
import heapq
# 1. 初始化堆(将列表转化为小顶堆,in-place操作)
heap = [3, 1, 4, 2]
heapq.heapify(heap)  # 转化后:[1, 2, 4, 3](满足小顶堆规则)
# 2. 插入元素(自动向上调整)
heapq.heappush(heap, 0)  # 插入后:[0, 1, 4, 3, 2]
# 3. 删除并返回堆顶(最小元素,自动向下调整)
min_val = heapq.heappop(heap)  # 返回0,堆变为[1, 2, 4, 3]
# 4. 查看堆顶(不删除)
print(heap[0])  # 输出1(当前堆顶)

2. 大顶堆实现(间接方式)

通过 "存储元素的负数",利用小顶堆的逻辑实现大顶堆效果:

ini 复制代码
import heapq
# 初始化大顶堆(存入负数)
max_heap = [-3, -1, -4, -2]
heapq.heapify(max_heap)  # 转化后:[-4, -2, -3, -1](小顶堆的"小"对应原数的"大")
# 插入元素(存负数)
heapq.heappush(max_heap, -0)  # 插入后:[-4, -2, -3, -1, 0]
# 删除并返回堆顶(原数的最大值,取负后返回)
max_val = -heapq.heappop(max_heap)  # 返回4,堆变为[-3, -2, 0, -1]
# 查看堆顶(原数的最大值)
print(-max_heap[0])  # 输出3

五、关键总结

  1. 结构本质:堆是 "完全二叉树" 的数组实现,核心是 "父节点与子节点的固定优先级关系"。
  1. 操作效率:插入、删除堆顶的时间复杂度均为 O (log n),远优于 "直接排序后取极值" 的 O (n log n)。
  1. 场景适配:求极值、Top K、优先级队列用堆;Python 需注意 heapq 仅支持小顶堆,大顶堆需间接实现。
  1. 局限性:堆仅能高效获取 "堆顶极值",若需查找任意元素,效率为 O (n)(需遍历数组),此时需结合哈希表等结构优化。
相关推荐
乔巴先生248 小时前
LLMCompiler:基于LangGraph的并行化Agent架构高效实现
人工智能·python·langchain·人机交互
Mr_Xuhhh9 小时前
项目需求分析(2)
c++·算法·leetcode·log4j
c++bug9 小时前
六级第一关——下楼梯
算法
Morri39 小时前
[Java恶补day53] 45. 跳跃游戏Ⅱ
java·算法·leetcode
林木辛9 小时前
LeetCode热题 15.三数之和(双指针)
算法·leetcode·双指针
张子夜 iiii10 小时前
实战项目-----Python+OpenCV 实现对视频的椒盐噪声注入与实时平滑还原”
开发语言·python·opencv·计算机视觉
AndrewHZ10 小时前
【3D算法技术】blender中,在曲面上如何进行贴图?
算法·3d·blender·贴图·三维建模·三维重建·pcg
Jared_devin10 小时前
二叉树算法题—— [蓝桥杯 2019 省 AB] 完全二叉树的权值
数据结构·c++·算法·职场和发展·蓝桥杯
困鲲鲲11 小时前
Flask 核心基础:从 路由装饰器 到 __name__ 变量 的底层逻辑解析
python·flask
AI 嗯啦11 小时前
数据结构深度解析:二叉树的基本原理
数据结构·算法