首页/数据结构/04-tree/二叉树的存储结构 🔗 在 Obsidian 中打开
数据结构 · 树与二叉树

二叉树的存储结构

难度 ★★重要度 ★★★★ 考查频率 高 二叉树顺序存储链式存储
速查
顺序存储按层序编号放数组(下标 1 开始,孩子 $2i$ / $2i+1$,双亲 $\lfloor i/2\rfloor$),只适合完全 / 满二叉树;二叉链表最通用,$n$ 个结点恰有 $n+1$ 个空指针

一、顺序存储

定义

用一组连续的存储单元,按层序编号存储二叉树的结点。

#define MaxSize 100
typedef struct {
    ElemType data[MaxSize];  // 从下标 1 开始存储
    int n;                   // 结点数
} SqBiTree;

编号规则

  • 根结点编号为 1
  • 结点 $i$ 的左孩子:$2i$
  • 结点 $i$ 的右孩子:$2i+1$
  • 结点 $i$ 的双亲:$\lfloor i/2 \rfloor$

示例

           A
         /   \
        B     C
       / \     \
      D   E     F

存储为:[_, A, B, C, D, E, _, F](下标 0 不用;6 号位空缺,因为 C 没有左孩子)

优缺点与适用场景

树形顺序存储表现
完全二叉树✅ 最适合,空间无浪费
满二叉树✅ 最适合
普通二叉树⚠️ 可能浪费大量空间(用 0 / 空填充)
极端情形一棵深度为 $k$ 的单支树(只有右孩子)只有 $k$ 个结点,却要占 $2^k-1$ 个存储单元 —— 这就是普通二叉树不用顺序存储的原因。

三、三叉链表(带父指针)

typedef struct TriTNode {
    ElemType data;
    struct TriTNode *lchild, *rchild, *parent;  // 增加父指针
} TriTNode, *TriTree;
┌──────────┬──────────┬──────────┬──────────┐
│  parent  │  lchild  │   data   │  rchild  │
│  双亲指针 │  左孩子   │  数据域   │  右孩子   │
└──────────┴──────────┴──────────┴──────────┘
  • 优点:查找双亲由 $O(n)$ 降为 $O(1)$。
  • 缺点:每结点多占一个指针,总共 $3n$ 个指针域。

四、存储结构对比

存储方式适用场景查找孩子查找双亲空间
顺序存储完全 / 满二叉树$O(1)$$O(1)$可能浪费
二叉链表通用$O(1)$$O(n)$$n+1$ 个空指针
三叉链表需频繁找双亲$O(1)$$O(1)$$3n$ 个指针
一句话选型形状规整(完全 / 满)→ 顺序;形状任意 → 二叉链表;要频繁上溯找祖先 → 三叉链表。

手算示例

例 1:顺序存储空间计算

完全二叉树有 100 个结点,用顺序存储需要多少空间?

  • 深度 $k = \lfloor \log_2 100 \rfloor + 1 = 6+1 = 7$
  • 同深度满二叉树结点数:$2^7-1 = 127$
  • 但完全二叉树编号连续,只需存到 100 号
  • 需要空间:$100+1 = 101$ 个存储单元(下标 0 不用)

例 2:空指针计算

二叉树有 50 个结点,二叉链表中有多少空指针?
空指针数 $= n+1 = \textbf{51}$。

例 3:由顺序存储还原二叉树

顺序存储:[_, A, B, C, D, E, _, F, G]

下标 $i$结点双亲 $\lfloor i/2\rfloor$是左还是右
1A
2B1 = A左($2=2\times1$)
3C1 = A右($3=2\times1+1$)
4D2 = B
5E2 = B
63 = CC 无左孩子
7F3 = C
8G4 = D左($8=2\times4$)
           A
         /   \
        B     C
       / \     \
      D   E     F
     /
    G
勘误提示源笔记此例把 G 画在了 F 下面。按编号规则 $\lfloor 8/2\rfloor = 4$,G 的双亲是 4 号结点 D,不是 F(F 在 7 号,其左孩子应在 14 号)。本页已按公式修正。还原树时一律回到 $\lfloor i/2\rfloor$ 算双亲,不要凭图形直觉。

常见考法

考法 1 · 空指针数问:$n$ 个结点的二叉链表有多少空指针? 答:$n+1$ 个。
考法 2 · 存储方式选择问:完全二叉树用什么存储最合适? 答:顺序存储
考法 3 · 顺序存储编号问:结点 $i$ 的左孩子编号? 答:$2i$(需满足 $2i \le n$,否则无左孩子)。
考法 4 · 指针域利用率问:二叉链表的指针利用率? 答:$\dfrac{n-1}{2n} \approx 50\%$ —— 一半指针是空的。

易错点

必记
  1. 顺序存储从下标 1 开始:下标 0 通常不用,否则孩子公式要改为 $2i+1$ / $2i+2$。
  2. 空指针数是 $n+1$:不是 $n$,也不是 $2n$。
  3. 普通二叉树不适合顺序存储:最坏浪费到 $2^k-1$。
  4. 二叉链表找双亲是 $O(n)$:需要遍历整棵树。
  5. 三叉链表空间开销 $3n$:用空间换找双亲的时间。

核心结论

  1. 顺序存储:按层序编号,孩子 $2i$ / $2i+1$,双亲 $\lfloor i/2\rfloor$,适合完全二叉树。
  2. 二叉链表:最常用,$n$ 个结点有 $n+1$ 个空指针。
  3. 三叉链表:增加 parent 指针,找双亲 $O(1)$。
  4. 空间效率:二叉链表指针利用率约 50%。

记忆卡片

n 个结点的二叉链表有多少空指针?
$n+1$ 个。
完全二叉树用什么存储最合适?
顺序存储。
顺序存储中结点 i 的右孩子编号?
$2i+1$(左孩子 $2i$,双亲 $\lfloor i/2\rfloor$)。
二叉链表找双亲的时间复杂度?
$O(n)$,需要遍历。
为什么空指针数是 n+1?
总指针 $2n$,非空指针 $n-1$(除根每结点被指一次),$2n-(n-1)=n+1$。
三叉链表比二叉链表多了什么?
parent 指针;空间 $3n$,换来找双亲 $O(1)$。

交互动画 · 顺序编号 ↔ 树结点对照

A B C D E F
点击结点字母:橙色 = 该结点,绿色 = 它的孩子位,米色 = 它的双亲位
观察点C 会看到左孩子位(下标 6)是空的 —— 这正是普通二叉树用顺序存储产生浪费的最小例子。点「标出空指针」则看到二叉链表的 $n+1=7$ 个空指针域。

相关知识点

binary-tree-properties binary-tree-traversal tree-storage-structure

↑ 以上为站内 HTML 相对链接(纯网页可浏览);本页右上「在 Obsidian 中打开」跳回源笔记。