别再用暴力排序了!大小顶堆让「取极值」效率飙升至 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)(需遍历数组),此时需结合哈希表等结构优化。
相关推荐
元亓亓亓17 分钟前
LeetCode热题100--101. 对称二叉树--简单
算法·leetcode·职场和发展
麻辣清汤30 分钟前
结合BI多维度异常分析(日期-> 商家/渠道->日期(商家/渠道))
数据库·python·sql·finebi
钢铁男儿40 分钟前
Python 正则表达式(正则表达式和Python 语言)
python·mysql·正则表达式
钢铁男儿1 小时前
Python 正则表达式实战:解析系统登录与进程信息
开发语言·python·正则表达式
不会学习?1 小时前
算法03 归并分治
算法
前端小趴菜051 小时前
python - range
python
☺����1 小时前
实现自己的AI视频监控系统-第一章-视频拉流与解码1
人工智能·python·音视频
NuyoahC1 小时前
笔试——Day43
c++·算法·笔试
前端小趴菜052 小时前
python - 元组常用操作
python
2301_821919922 小时前
决策树8.19
算法·决策树·机器学习