树的存储结构主要有三种:双亲表示法、孩子表示法、孩子兄弟表示法。
用一组连续空间存储结点,每个结点含 data 与 parent(父结点下标,根为 -1):
#define MaxSize 100
typedef struct { ElemType data; int parent; } PTNode; // parent:父结点下标,根为 -1
typedef struct { PTNode nodes[MaxSize]; int n; } PTree;
示例树 A(B(D,E),C(F)) 的双亲表示:
| 下标 | data | parent |
|---|---|---|
| 0 | A | -1 |
| 1 | B | 0 |
| 2 | C | 0 |
| 3 | D | 1 |
| 4 | E | 1 |
| 5 | F | 2 |
优点:找父结点 O(1);缺点:找孩子需遍历整个数组 O(n)。
typedef struct CTNode { int child; struct CTNode *next; } CTNode;
typedef struct { ElemType data; CTNode *firstChild; } CTBox; // 孩子链表头
typedef struct { CTBox nodes[MaxSize]; int n, r; } CTree; // r = 根位置
下标 data 孩子链表 0 A → [1] → [2] → NULL 1 B → [3] → [4] → NULL 2 C → [5] → NULL 3 D → NULL …
优点:找孩子方便;缺点:找父结点需遍历。
typedef struct CSNode {
ElemType data;
struct CSNode *firstChild, *nextSibling; // 第一个孩子、下一个兄弟
} CSNode, *CSTree;
示例:A(B,C,D) 且 B 有孩子 E、F:
A
/
B ← B 的左孩子 = 第一个孩子;右孩子 = 下一个兄弟 C
/ \
D E ← 等价二叉树:D→E 相当于"E 是 D 的下一个兄弟"…
| 表示法 | 找父结点 | 找孩子 | 空间 |
|---|---|---|---|
| 双亲表示法 | O(1) | O(n) | O(n) |
| 孩子表示法 | O(n) | O(1) | O(n+e) |
| 孩子兄弟表示法 | O(n) | O(第一个孩子) | O(n) |
1
/ \
2 3
/ / \
4 5 6
| 下标 | data | parent |
|---|---|---|
| 0 | 1 | -1 |
| 1 | 2 | 0 |
| 2 | 3 | 0 |
| 3 | 4 | 1 |
| 4 | 5 | 2 |
| 5 | 6 | 2 |
A
/ | \
B C D
/ \
E F
→
A
/
B
/ \
E C
\ \
F D
双亲表示中求 E(下标 4)的祖先路径:$E(4) \to parent=1(B) \to parent=0(A) \to parent=-1$(结束)→ 路径 E → B → A。
| 表示法 | 核心思想 | 最适合场景 |
|---|---|---|
| 双亲表示法 | 每个结点记录父结点下标 | 频繁找父结点 |
| 孩子表示法 | 每个结点维护孩子链表 | 频繁找孩子 |
| 孩子兄弟表示法 | 左孩子右兄弟,可转二叉树 | 树与二叉树转换 |
(暂无关联知识点)
↑ 以上为站内 HTML 相对链接(纯网页可浏览);本页右上「在 Obsidian 中打开」跳回源笔记。