发布于2026-05-20 阅读(0)
扫一扫,手机访问
用数组实现最大堆,本质上是在一维空间里模拟一棵完全二叉树,并通过一套简单的索引计算规则,维持“父节点永远大于等于子节点”的核心秩序。这种结构的设计初衷,并非为了在静态数据里找一次最大值——那种情况用 max() 函数就够了。它的真正威力,在于能动态、高效地维护一个持续变化的数据集,让你能以极快的速度反复获取当前的最大值。无论是实时更新的排行榜、优先任务队列,还是流式数据中的峰值监控,数组堆都是背后的经典引擎。

一切操作都建立在索引映射这个“地基”之上。假设数组索引从0开始(这也是PHP等多数语言的默认方式),那么对于数组中任意位置 i 的元素,其家庭成员的位置可以通过固定公式瞬间定位:
(int)(($i - 1) / 2)$i * 2 + 1$i * 2 + 2举个例子就清楚了:索引0是根节点,它的左孩子是1,右孩子是2。索引3的父节点是 (3-1)/2 = 1;索引4的父节点同样是 (4-1)/2 = 1(整除结果)。这套关系必须烂熟于心,后续所有的“上浮”和“下沉”调整,都靠它来驱动。
当有新成员要加入时,策略很直接:先把它放到数组末尾,再视情况把它“托举”到合适的高度。因为新元素可能比它的父节点大,这就破坏了堆序性,需要一次“上浮”操作来修复。
array_push($heap, $value),将新值放到堆尾。$i = count($heap) - 1。$heap[$i] > $heap[(int)(($i-1)/2)](即比父节点大),就与父节点交换位置,并将当前位置 $i 更新为父节点的索引。来看个实例:向最大堆 [50, 30, 40, 10, 20, 35] 中插入15。追加后数组变为 [50, 30, 40, 10, 20, 35, 15]。新元素15位于索引6,其父节点是索引2(值为40)。由于15小于40,不满足交换条件,插入过程就此完成,堆序性依然完好。
最大值永远稳坐堆顶(索引0)。删除它时有个小技巧:不能简单地将它从数组中移除,那样会破坏完全二叉树的结构。正确的做法是“李代桃僵”。
$max = $heap[0]。$heap[0] = array_pop($heap)。siftDown(0)),让它找到自己真正该待的位置。下沉的逻辑是:比较当前节点与其左右子节点中较大的那一个。如果当前节点比那个子节点小,就交换它们的位置,然后继续从新的子节点位置向下比较。这个过程一直持续到当前节点大于等于它的所有子节点,或者已经沉到底成为叶子节点为止。
实现时需注意边界:左子索引 $left = $i * 2 + 1 必须小于当前堆的大小,右子索引同理。如果只有左子节点,那就只和左子比较。
让我们设想一个实际场景:你正在监控一系列实时传入的温度读数,需要随时知道当前最高的温度值,并且这个数据集会不断新增,偶尔还需要移除无效的峰值。
$tempHeap = []; 创建一个空堆。insert($tempHeap, 28); insert($tempHeap, 32); insert($tempHeap, 29); … 每个新读数都通过插入操作入堆。$tempHeap[0] ?? null;。这是O(1)时间复杂度,无需遍历整个数组。$removed = deleteMax($tempHeap);。堆会自动内部重组,之后 $tempHeap[0] 给出的就是新的最大值。与每次调用 max($array) 都需要全量扫描O(n)相比,堆结构在频繁增删的场景下优势巨大。它将“获取最大值”的成本锁定在常数时间,而“删除并重组”的成本也仅是对数级别,堪称管理动态极值问题的利器。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8