下标从0开始时,左子节点索引为2i+1、右子节点为2i+2,源于层序编号规律:根在0,第1层占索引1~2,第2层占3~6,依此类推;每个节点i的左右子必落在其“已占位置扩展”后的确定槽位,且(i−1)/2向下取整可统一求父节点。

完全二叉树用数组存储时,节点位置不是随意排列的,而是严格遵循层序编号规律。这种映射关系让父子、左右子节点的坐标能通过简单算术直接得出,不需要指针或额外空间。
为什么下标从 0 开始时,左子是 2i+1、右子是 2i+2
这是由层序遍历的编号逻辑决定的。假设根节点在索引 0,它之后的第 1 层有 2 个节点(索引 1 和 2),第 2 层最多有 4 个节点(索引 3–6),依此类推。每个节点 i 的左子必然紧接在它“已占位置”的两倍加一的位置上——因为前面 i 个节点共“撑开”了左子应处的起始槽位。例如:
- 根节点 i=0 → 左子在 2×0+1 = 1,右子在 2×0+2 = 2
- i=1(第 1 层左节点)→ 左子在 2×1+1 = 3,右子在 4
- i=2(第 1 层右节点)→ 左子在 5,右子在 6
父节点怎么算?统一用 (i−1)/2 向下取整
无论 i 是左子还是右子,它的父节点都落在同一位置:把 i 减 1 后除以 2,再向下取整。这是因为左子(奇数下标)和右子(偶数下标)在减 1 后都变成偶数,整除 2 就自然归到同一个父位置。例如:
- i=3 → (3−1)/2 = 1
- i=4 → (4−1)/2 = 1.5 → ⌊1.5⌋ = 1
- i=5 → (5−1)/2 = 2
- i=6 → (6−1)/2 = 2.5 → ⌊2.5⌋ = 2
所以索引 3 和 4 共享父节点 1,5 和 6 共享父节点 2——完全符合完全二叉树结构。
数组长度与堆大小的区别要分清
数组本身可能比实际使用的节点数更长,比如声明了长度为 10 的数组,但只存了 6 个有效元素。这时:
- LENGTH(A) 是数组总容量(如 10)
- HEAP_SIZE(A) 是当前堆中有效节点数(如 6)
所有坐标换算(如找子节点)都以 HEAP_SIZE 为边界判断是否越界。比如 i=5 时,左子索引 2×5+1 = 11,但若 HEAP_SIZE=6,则 11 ≥ 6,说明无左子。
非完全二叉树不适合这种映射
这套公式只对完全二叉树成立。如果树中间有空缺(比如第 2 层只有左子没右子,第 3 层却突然出现节点),数组里就会产生大量无效空位,不仅浪费空间,还会让 (i−1)/2 等计算指向错误父节点。实际应用中,堆、优先队列等依赖高效随机访问的结构,都强制要求底层是完全二叉树,正是为了守住这个映射的确定性。










