树(Tree)
1.1 树的基本概念
定义
树是n(n≥0)个结点的有限集合。
n = 0:空树
n > 0:满足以下条件:
-
有且仅有一个特定的根结点
-
其余结点可分为m个互不相交的有限集合T₁,T₂,...,Tₘ,每个集合又是一棵树,称为子树
术语
-
结点的度:结点拥有的子树个数
-
叶结点:度为0的结点
-
分支结点:度不为0的结点
-
树的度:树中最大的结点度数
-
结点的层次:根为第1层,孩子为第2层,以此类推
-
树的高度/深度:树中结点的最大层次
-
有序树 vs 无序树:子树有顺序为有序树,否则为无序树
-
森林:m(m≥0)棵互不相交的树的集合
树的特征
-
动态储存:O(1)的插入和删除(在已知位置)
-
查找速度:O(log n)(平衡树)
-
层次结构:天然表达层次关系
1.2 树的存储结构
1. 双亲表示法(顺序存储)
#define MAXSIZE 100
typedef char DataType;
typedef struct {
DataType data;
int parent; // 双亲位置,根为-1
} PTNode;
typedef struct {
PTNode nodes[MAXSIZE];
int n; // 结点数
} PTree;
特点:
-
查找双亲快:O(1)
-
查找孩子慢:需要遍历整个数组
2. 孩子表示法(链式+顺序):
// 孩子结点
typedef struct CTNode {
int child; // 孩子在数组中的位置
struct CTNode *next; // 下一个孩子
} *ChildPtr;
// 表头结构
typedef struct {
DataType data;
ChildPtr firstchild; // 第一个孩子
} CTBox;
typedef struct {
CTBox nodes[MAXSIZE];
int n, root; // 结点数和根位置
} CTree;
特点:
-
查找孩子快
-
查找双亲慢
3. 孩子兄弟表示法(二叉树表示法)
typedef struct CSNode {
DataType data;
struct CSNode *firstchild; // 第一个孩子
struct CSNode *nextsibling; // 右兄弟
} CSNode, *CSTree;
特点:
-
将树转换为二叉树
-
便于操作和遍历
二、二叉树(Binary Tree)
2.1 二叉树定义
n个结点的有限集合,该集合:
-
要么为空树(n=0)
-
要么由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成
2.2 二叉树特点
-
每个结点最多有两个子树
-
左子树和右子树有顺序,不能颠倒
-
即使只有一个子树,也要区分左/右
2.3 特殊二叉树
1. 斜树
-
左斜树:所有结点都只有左子树
-
右斜树:所有结点都只有右子树
相当于线性表
2. 满二叉树
-
所有分支结点都有左右子树
-
所有叶子结点都在同一层
-
深度为k的满二叉树有 2ᵏ - 1 个结点
3. 完全二叉树
对n个结点按层序编号(1~n)
编号i的结点与同样深度的满二叉树中编号i的结点位置完全相同
特点:
-
叶子结点只能出现在最下两层
-
最下层叶子结点集中在左侧连续位置
-
如果有度为1的结点,只能有一个,且只有左孩子
2.4 二叉树性质
-
性质1:第i层上最多有 2ⁱ⁻¹ 个结点(i≥1)
-
性质2:深度为k的二叉树至多有 2ᵏ - 1 个结点(k≥1)
-
性质3:对任何二叉树,如果叶子结点数为n₀,度为2的结点数为n₂,则:n₀ = n₂ + 1
-
性质4:具有n个结点的完全二叉树深度为 ⌊log₂n⌋ + 1
-
性质5:对完全二叉树编号(1~n):
-
i=1:根结点
-
i>1:双亲为 ⌊i/2⌋
-
2i≤n:左孩子为2i
-
2i+1≤n:右孩子为2i+1
2.5 二叉树存储
1. 顺序存储:
#define MAXSIZE 100
typedef char DataType;
// 完全二叉树用数组存储
DataType tree[MAXSIZE + 1]; // 下标从1开始
-
适用:完全二叉树
-
不适用:斜树(空间浪费)
2. 链式存储(二叉链表):
typedef struct BiTNode {
DataType data;
struct BiTNode *lchild, *rchild; // 左右孩子指针
} BiTNode, *BiTree;
2.6 二叉树遍历
深度优先遍历(DFS)
1. 先序遍历(根左右):
void PreOrder(BiTree T) {
if (T == NULL) return;
printf("%c ", T->data); // 访问根
PreOrder(T->lchild); // 遍历左子树
PreOrder(T->rchild); // 遍历右子树
}
2. 中序遍历(左根右):
void InOrder(BiTree T) {
if (T == NULL) return;
InOrder(T->lchild); // 遍历左子树
printf("%c ", T->data); // 访问根
InOrder(T->rchild); // 遍历右子树
}
3. 后序遍历(左右根):
void PostOrder(BiTree T) {
if (T == NULL) return;
PostOrder(T->lchild); // 遍历左子树
PostOrder(T->rchild); // 遍历右子树
printf("%c ", T->data); // 访问根
}
广度优先遍历(层序遍历)
void LevelOrder(BiTree T) {
if (T == NULL) return;
BiTree queue[MAXSIZE]; // 队列
int front = 0, rear = 0;
queue[rear++] = T; // 根结点入队
while (front < rear) {
BiTree node = queue[front++];
printf("%c ", node->data);
if (node->lchild != NULL)
queue[rear++] = node->lchild;
if (node->rchild != NULL)
queue[rear++] = node->rchild;
}
}
2.7 遍历序列确定二叉树
-
先序+中序:可唯一确定二叉树
-
后序+中序:可唯一确定二叉树
-
先序+后序:不能唯一确定(除非是满二叉树)