文章目录
- 树的逻辑结构
- 双亲表示法(顺序存储)
- [孩子表示法(顺序 + 链式存储)](#孩子表示法(顺序 + 链式存储))
- 孩子兄弟表示法(链式存储)
树的逻辑结构
树是一种递归定义的数据结构。
树是n (n≥0)个结点 的有限集合,n=0时,称为空树 ,这是一种特殊情况。在任意一棵非空树中应满足:
- 有且仅有一个特定的称为根的结点。
- 当n>1时,其余结点可分为m(m>0)个互不相交的有限集合 T1,T2,...Tm,其中每个集合本身又是一棵树,并且称为根结点的子树。

二叉树:一个分支结点最多可能有两棵子树 。
树:一个分支结点可以有多棵子树。【只依靠数组下标,无法反映结点之间的逻辑关系】
在树当中,除了根结点外,其他任何一个结点都有且仅有一个双亲结点(有且仅有一个父结点)。
双亲表示法(顺序存储)
如何实现树的存储结构?
- 用一维数组顺序存储各个结点。每个结点中保存数据元素、指向双亲结点(父节点)的"指针"。
cpp
#define MAX_TREE_SIZE 100 // 树中最多结点树
// 树的结点定义
typedef struct {
ElemType data; // 数据域 存储数据元素
int parent; // 父结点在数组中的下标(根结点 parent = -1)
} PTNode;
// 树的类型定义
typedef struct {
PTNode nodes[MAX_TREE_SIZE]; // 数组存储所有结点
int n; // 当前结点个数
} PTree;

双亲表示法的优缺点:
- 优点:查找双亲(父节点)极其方便(
O(1))。 - 缺点:查找某个结点的所有孩子,必须遍历整个数组(
O(n)),效率较低。 - 适用于"找父亲"多,"找孩子"少的应用场景。如:并查集
拓展:双亲表示法存储"森林"
每棵树的根节点双亲指针 = -1

孩子表示法(顺序 + 链式存储)
孩子表示法:用数组顺序存储各个结点。每个结点中保存数据元素、孩子链表头指针。
将每个结点的所有孩子,用一个单链表串起来。
cpp
// 1. 孩子链表结点(存储数组下标)
typedef struct CTNode {
int child; // 孩子在数组中的下标
struct CTNode *next; // 指向下一个兄弟
} *ChildPtr;
// 2. 表头结构(数组元素)
typedef struct {
ElemType data; // 数据域
ChildPtr firstChild; // 指向第一个孩子的指针
} CTBox;
typedef struct {
CTBox nodes[MAX_TREE_SIZE]; // 数组
int n, r; // 结点总数 和 根的位置
} CTree;

孩子表示法的优缺点:
- 优点:查找某个结点的所有孩子非常方便。。
- 缺点:查找父结点需要遍历整个结构(
O(n))。 - 适用于"找孩子"多,"找父亲"少的应用场景。如:服务流程树
拓展:孩子表示法存储"森林"。
【注】用孩子表示法存储森林,需要记录多个根的位置

孩子兄弟表示法(链式存储)
树的孩子兄弟表示法 ,与二叉树类似,采用二叉链表 实现。
每个结点内保存数据元素 和两个指针,但两个指针的含义与二叉树结点不同。
cpp
// 树的存储------孩子兄弟表示法
typedef struct CSNode {
ElemType data; // 数据域
struct CSNode *firstchild, *nextsibling;
// 左:指向第一个孩子;右:指向右边一个兄弟
} CSNode, *CSTree;
// 二叉树结点(链式存储)
typedef struct BiTNode{
ElemType data;
struct BiTNode *lchild,*rchild; // 左子树 右子树
}BiTNode,*BiTNode;

拓展:孩子兄弟表示法存储"森林"。
【注】用"孩子兄弟表示法"存储 树或森林 时,从存储视角来看形态上与二叉树类似。
森林中的每棵树的根节点是为平级的兄弟关系。
