堆物理上是数组、逻辑上是完全二叉树;下标公式0起始时左孩子=2×parent+1、右孩子=2×parent+2、父节点=(child−1)/2,源于层序编号的数学推导,是建堆与调整的理论基础。

堆在物理上是数组,逻辑上是完全二叉树;下标映射公式不是凭空记忆的规则,而是由层序遍历的编号方式自然导出的数学关系。掌握它,才能真正理解建堆、调整、排序每一步为何那样写。
为什么下标从 0 开始时,左孩子是 2×parent + 1?
完全二叉树按层序遍历编号:根为 0,第二层从左到右是 1、2,第三层是 3、4、5、6……观察每一层起始下标:
- 第 0 层(根):1 个节点,起始下标 = 0
- 第 1 层:2 个节点,起始下标 = 1 = 2⁰
- 第 2 层:4 个节点,起始下标 = 3 = 2⁰ + 2¹
- 第 h 层起始下标 = 2⁰ + 2¹ + … + 2h−1 = 2h − 1
而 parent 所在层为 h,则其左孩子必在第 h+1 层,且是该层第一个子节点——即 parent 的左孩子在层内偏移为 0,对应下标为:
2h+1 − 1 + 0 = 2×(2h − 1) + 1 = 2×parent + 1
同理,右孩子偏移为 1 → 下标 = 2×parent + 2。
父节点下标为什么是 (child − 1) / 2?
设 child 下标为 c,它一定有唯一父节点 p。根据上式,c 要么是 2p+1(左),要么是 2p+2(右)。
- 若 c = 2p + 1 → p = (c − 1)/2
- 若 c = 2p + 2 → p = (c − 2)/2 = (c − 1)/2(整数除法向下取整,因 c 为偶数时 c−1 是奇数,(c−1)/2 仍得正确整数结果)
所以统一写作:p = (c − 1) / 2(整除,C/Java/Python 中对非负整数自动向下取整)。
下标从 1 开始时公式为何不同?
若根节点放在下标 1,层序编号变为:1, 2, 3, 4, 5, 6, 7…
- 第 h 层起始下标 = 2h−1
- parent = i → 左孩子在第 h+1 层第 (i − 2h−1 + 1) × 2 个位置 → 推得 left = 2i,right = 2i + 1
- 反推:child = i → parent = i / 2(整除)
两种约定都合理,但工业代码(如 C++ STL、Java PriorityQueue)和多数教材默认下标从 0 开始,因此 2i+1 / 2i+2 / (i−1)/2 是必须熟记的标准映射。
最后一个非叶子节点下标为什么是 (n − 2) / 2?
n 个节点的完全二叉树,叶子节点集中在最后一层及倒数第二层右侧。最后一个有孩子的节点,一定是最后一个叶子节点的父节点。
- 最后一个节点下标是 n−1
- 它的父节点就是 (n−1 − 1) / 2 = (n−2) / 2
- 这个节点之后的所有下标(包括它自己)都不再有左孩子(否则总节点数会超 n)
所以建堆时,只需从下标 (n−2)/2 开始向前做向下调整——这是自底向上修复堆性质的起点。










