别再用暴力排序了!大小顶堆让「取极值」效率飙升至 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)(需遍历数组),此时需结合哈希表等结构优化。
相关推荐
Jul1en_几秒前
【算法】位运算
算法
苏州知芯传感5 分钟前
柔性抓取的“慧眼”:MEMS 3D视觉如何让机器人精准识别无序堆叠的复杂钣金件?
算法·3d·机器人·mems
天呐草莓9 分钟前
企业微信运维手册
java·运维·网络·python·微信小程序·企业微信·微信开放平台
2501_9216494910 分钟前
股票 API 对接, 接入德国法兰克福交易所(FWB/Xetra)实现量化分析
后端·python·websocket·金融·区块链
lbb 小魔仙12 分钟前
Python 读取 Excel 文件:openpyxl 与 pandas 实战对比
python·excel·pandas
iAkuya13 分钟前
(leetcode)力扣100 22相交链表(双指针)
算法·leetcode·链表
程序员miki13 分钟前
Redis核心命令以及技术方案参考文档(分布式锁,缓存业务逻辑)
redis·分布式·python·缓存
热爱生活的五柒15 分钟前
两个电脑(windows和linux之间)如何快速传输文件(亲测可用,方便快捷)
python·共享文件夹
chao18984423 分钟前
基于MATLAB的ADI方法求解偏微分方程详解
开发语言·算法·matlab
洋生巅峰23 分钟前
股票爬虫实战解析
爬虫·python·mysql