堆用数组实现时父子下标关系由层序编号自然导出:父节点i的左孩子为2i+1、右孩子为2i+2,子节点j的父节点为(j−1)/2(整除),且需满足边界条件2i+1<n等以避免越界。

堆用数组实现时,父子节点的下标关系不是凭空约定的,而是由完全二叉树的层序编号方式自然导出的。理解这个推导过程,才能真正掌握堆的操作逻辑,而不是死记公式。
为什么从 0 开始编号?
现代编程语言(如 C、Java、Python)中数组默认下标从 0 起始,堆的数组实现也遵循这一惯例。这会影响父子映射公式的具体形式——若从 1 开始编号,公式会更“对称”,但实际工程中几乎都采用 0 起始,因此必须适配它。
关键前提是:节点按**层序遍历顺序**填入数组。根是第 0 个,接着是第 1 层两个节点(索引 1、2),再是第 2 层最多四个节点(索引 3–6),依此类推。
左孩子 = 2 × 父 + 1 的由来
设父节点位于第 d 层(根为第 0 层),该层起始索引为 2d − 1(0 起始下标下,第 d 层共 2d 个位置,前面 d 层共 2d − 1 个节点)。父节点在本层偏移为 k(0 ≤ k d),则其数组下标为:
i = (2d − 1) + k
它的左孩子在第 d+1 层,且占据该层第 2k 个位置(因为每个父节点对应两个孩子,左孩子排在右孩子之前)。第 d+1 层起始索引是 2d+1 − 1,所以左孩子下标为:
left = (2d+1 − 1) + 2k = 2×(2d − 1 + k) + 1 = 2i + 1
同理可得右孩子下标为 2i + 2。
父节点 = ⌊(i − 1)/2⌋ 的验证逻辑
已知孩子下标 i,反推其父节点位置。无论 i 是左还是右孩子,其父节点在上一层的偏移都是 ⌊i/2⌋(整除)。代入层序关系可严格推出:
- 若 i 是左孩子 → i = 2p + 1 → p = (i − 1)/2
- 若 i 是右孩子 → i = 2p + 2 → p = (i − 2)/2
两者合并,统一写为 p = ⌊(i − 1)/2⌋(整数除法向下取整,C/Java 中 (i-1)/2 自动满足)。
这个公式对所有非根节点(i > 0)都成立;根节点无父,无需计算。
实战中必须检查边界
公式给出的是理论位置,实际使用前必须判断是否越界:
- 左孩子存在 ⇔ 2i + 1 < n(n 是当前堆大小)
- 右孩子存在 ⇔ 2i + 2 < n
- 叶子节点 ⇔ 左孩子不存在(一旦 2i+1 ≥ n,就说明该节点无子,是叶子)
这是堆化(heapify)、上浮(sift-up)、下沉(sift-down)过程中终止递归或循环的关键条件,漏掉会导致数组访问越界。

















