文章目录
6.1 树的基本概念
6.1.1 树的定义和基本术语
1. 树的定义
树(Tree)是 n n n ( n ≥ 0 n\ge0 n≥0) 个结点的有限集合 T T T,若 n = 0 n=0 n=0 时称为空树,否则:
⑴ 有且只有一个特殊的称为树的根(Root)结点;
⑵ 若
n
>
1
n>1
n>1 时,其余的结点被分为
m
(
m
>
0
)
m(m>0)
m(m>0) 个互不相交的子集
T
1
,
T
2
,
T
3
,
…
,
T
m
T_1, T_2, T_3,…,T_m
T1,T2,T3,…,Tm,其中每个子集本身又是一棵树,称其为根的子树(Subtree)。
这是树的递归定义,即用树来定义树,而只有一个结点的树必定仅由根组成,如图6-1(a)所示。
2. 树的基本术语
⑴ 结点(node):一个数据元素及其若干指向其子树的分支。
⑵ 结点的度(degree) 、树的度:结点所拥有的子树的棵数称为结点的度。树中结点度的最大值称为树的度。
⑶ 叶子(leaf)结点、非叶子结点:树中度为0的结点称为叶子结点(或终端结点)。相对应地,度不为0的结点称为非叶子结点(或非终端结点或分支结点)。除根结点外,分支结点又称为内部结点。
⑷ 孩子结点、双亲结点、兄弟结点
一个结点的子树的根称为该结点的孩子结点(child)或子结点;相应地,该结点是其孩子结点的双亲结点(parent)或父结点。同一双亲结点的所有子结点互称为兄弟结点。
⑸ 层次、堂兄弟结点
双亲结点在同一层上的所有结点互称为堂兄弟结点。如图6-1(b)中结点 E、F、G、H、I、J。
⑹ 结点的层次路径、祖先、子孙
从根结点开始,到达某结点 p p p 所经过的所有结点成为结点 p p p 的层次路径(有且只有一条)。结点 p p p 的层次路径上的所有结点( p p p 除外)称为 p p p 的祖先(ancester) 。以某一结点为根的子树中的任意结点称为该结点的子孙结点(descendant)。
⑺ 树的深度(depth):树中结点的最大层次值,又称为树的高度
⑻ 有序树和无序树:对于一棵树,若其中每一个结点的子树(若有)具有一定的次序,则该树称为有序树,否则称为无序树。
⑼ 森林(forest):是 m ( m ≥ 0 ) m(m\ge 0) m(m≥0) 棵互不相交的树的集合。显然,若将一棵树的根结点删除,剩余的子树就构成了森林。
3. 树的表示形式
⑴ 倒悬树。是最常用的表示形式,如图6-1(b)。
⑵ 嵌套集合。是一些集合的集体,对于任何两个集合,或者不相交,或者一个集合包含另一个集合。图6-2(a)是图6-1(b)树的嵌套集合形式。
⑶ 广义表形式。图6-2(b)
⑷ 凹入法表示形式。见P120
树的表示方法的多样化说明了树结构的重要性。
6.2 二叉树
6.2.1 二叉树的定义
二叉树(Binary tree)是
n
(
n
≥
0
)
n(n≥0)
n(n≥0)个结点的有限集合。若n=0时称为空树,否则:
⑴ 有且只有一个特殊的称为树的根(Root)结点;
⑵ 若
n
>
1
n>1
n>1时,其余的结点被分成为二个互不相交的子集
T
1
T_1
T1,
T
2
T_2
T2,分别称之为左、右子树,并且左、右子树又都是二叉树。
由此可知,二叉树的定义是递归的。
二叉树在树结构中起着非常重要的作用。因为二叉树结构简单,存储效率高,树的操作算法相对简单,且任何树都很容易转化成二叉树结构。上节中引入的有关树的术语也都适用于二叉树。
6.2.2 二叉树的性质
性质1:在非空二叉树中,第 i i i 层上至多有 2 i − 1 2^{i-1} 2i−1 个结点 ( i ≥ 1 ) (i\ge1) (i≥1)。
可用数学归纳法证明,略。
性质2:深度为 k k k 的二叉树至多有 2 k − 1 2^k-1 2k−1 个结点( k ≥ 1 k\ge 1 k≥1) 。
性质3:对任何一棵二叉树,若其叶子结点数为 n 0 n_0 n0,度为2的结点数为 n 2 n_2 n2,则 n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1。
证明:设二叉树中度为 1 的结点数为 n 1 n_1 n1,二叉树中总结点数为 N N N,因为二叉树中所有结点度均小于或等于 2,则有: N = n 0 + n 1 + n 2 N=n_0+n_1+n_2 N=n0+n1+n2
再看二叉树中的分支数:除根结点外,其余每个结点都有唯一的一个进入分支,而所有这些分支都是由度为 1 和 2 的结点射出的。设B为二叉树中的分支总数,则有: N = B + 1 N=B+1 N=B+1
∴ B = n 1 + 2 × n 2 ∴ N = B + 1 = n 1 + 2 × n 2 + 1 ∴ n 0 + n 1 + n 2 = n 1 + 2 × n 2 + 1 ∴ n 0 = n 2 + 1 \begin{array}{ll} \therefore & B=n_{1}+2 \times n_{2} \\ \therefore & N=B+1=n_{1}+2 \times n_{2}+1 \\ \therefore & n_{0}+n_{1}+n_{2}=n_{1}+2 \times n_{2}+1 \\ \therefore & n_{0}=n_{2}+1 \end{array} ∴∴∴∴B=n1+2×n2N=B+1=n1+2×n2+1n0+n1+n2=n1+2×n2+1n0=n2+1
满二叉树和完全二叉树
- 满二叉树
一棵深度为 k k k 且有 2 k − 1 2^k-1 2k−1 个结点的二叉树称为满二叉树(Full Binary Tree)。
满二叉树的特点:
◆ 基本特点是每一层上的结点数总是最大结点数。
◆ 满二叉树的所有的支结点都有左、右子树。
◆ 可对满二叉树的结点进行连续编号,若规定从根结点开始,按“自上而下、自左至右”的原则进行。
- 完全二叉树
完全二叉树(Complete Binary Tree):如果深度为 k k k,由 n n n 个结点的二叉树,当且仅当其每一个结点都与深度为 k k k 的满二叉树中编号从 1 1 1 到 n n n 的结点一一对应,该二叉树称为完全二叉树。
或深度为 k k k 的满二叉树中编号从 1 1 1 到 n n n 的前 n n n 个结点构成了一棵深度为 k k k 的完全二叉树。其中 2 k − 1 ≤ n ≤ 2 k − 1 2^{k-1} \le n\le 2^{k}-1 2k−1≤n≤2k−1
完全二叉树是满二叉树的一部分,而满二叉树是完全二叉树的特例。
完全二叉树的特点:
若完全二叉树的深度为 k k k ,则所有的叶子结点都出现在第 k k k 层或 k − 1 k-1 k−1 层。对于任一结点,如果其右子树的最大层次为 l l l,则其左子树的最大层次为 l l l 或 l + 1 l+1 l+1。
性质4: n n n 个结点的完全二叉树深度为: ⌊ log 2 n ⌋ + 1 \left\lfloor\log _{2} n\right\rfloor+1 ⌊log2n⌋+1。
k = ⌊ log 2 n ⌋ + 1 k=\left\lfloor\log _{2} n\right\rfloor+1 k=⌊log2n⌋+1
性质5:若对一棵有 n n n 个结点的完全二叉树(深度为 ⌊ log 2 n ⌋ + 1 \left\lfloor\log _{2} n\right\rfloor+1 ⌊log2n⌋+1 的结点按层(从第 1 1 1 层到第 ⌊ log 2 n ⌋ + 1 \left\lfloor\log _{2} n\right\rfloor+1 ⌊log2n⌋+1 层)序自左至右进行编号,则对于编号为 i ( 1 ≤ i ≤ n ) i(1\le i\le n) i(1≤i≤n) 的结点:
(1) 若 i = 1 i=1 i=1:则结点 i i i 是二叉树的根,无双亲结点;否则,若 i > 1 i>1 i>1,则其双亲结点编号是 ⌊ i / 2 ⌋ \left\lfloor i/2\right\rfloor ⌊i/2⌋ 。
(2)如果 2 i > n 2i>n 2i>n:则结点 i i i 为叶子结点,无左孩子;否则,其左孩子结点编号是 2 i 2i 2i。
(3)如果 2 i + 1 > n 2i+1>n 2i+1>n:则结点 i i i 无右孩子;否则,其右孩子结点编号是 2 i + 1 2i+1 2i+1。
可以用数学归纳法证明。
6.2.3 二叉树的存储结构
1. 顺序存储结构
二叉树存储结构的类型定义:
#define MAX_SIZE 100
typedef telemtype sqbitree[MAX_SIZE];
用一组地址连续的存储单元依次“自上而下、自左至右”存储完全二叉树的数据元素。
对于完全二叉树上编号为 i i i 的结点元素存储在一维数组的下标值为 i − 1 i-1 i−1 的分量中,如图6-6(c)所示。
对于一般的二叉树,将其每个结点与完全二叉树上的结点相对照,存储在一维数组中,如图6-6(d)所示。
最坏的情况下,一个深度为 k k k 且只有 k k k 个结点的单支树需要长度为 2 k − 1 2^k-1 2k−1 的一维数组。
2. 链式存储结构
设计不同的结点结构可构成不同的链式存储结构。
① 二叉链表结点。有三个域:一个数据域,两个分别指向左右子结点的指针域,如图6-7(a)所示。
typedef struct BTNode
{
ElemType data;
struct BTNode *Lchild, *Rchild;
} BTNode;
② 三叉链表结点。除二叉链表的三个域外,再增加一个指针域,用来指向结点的父结点,如图6-7(b)所示。
typedef struct BTNode_3
{
ElemType data;
struct BTNode_3 *Lchild, *Rchild, *parent;
} BTNode_3;
6.3 遍历二叉树及其应用
(考试必考,给你两种遍历方式来给出第三种遍历方式)
遍历二叉树(Traversing Binary Tree):是指按指定的规律对二叉树中的每个结点访问一次且仅访问一次。
对于二叉树的遍历,分别讨论递归遍历算法和非递归遍历算法。递归遍历算法具有非常清晰的结构,但初学者往往难以接受或怀疑,不敢使用。实际上,递归算法是由系统通过使用堆栈来实现控制的。而非递归算法中的控制是由设计者定义和使用堆栈来实现的。
若以L、D、R分别表示遍历左子树、遍历根结点和遍历右子树,则有六种遍历方案:DLR、LDR、LRD、DRL、RDL、RLD。若规定先左后右,则只有前三种情况三种情况,分别是:
- DLR——先(根)序遍历。
- LDR——中(根)序遍历。
- LRD——后(根)序遍历。
6.3.1 先序遍历二叉树
1. 递归算法
算法的递归定义是:
若二叉树为空,则遍历结束;否则
⑴ 访问根结点;
⑵ 先序遍历左子树(递归调用本算法);
⑶ 先序遍历右子树(递归调用本算法)。
void PreorderTraverse(BTNode *T)
{
if (T != NULL)
{
visit(T->data); /* 访问根结点 */
PreorderTraverse(T->Lchild);
PreorderTraverse(T->Rchild);
}
}
说明:
visit()
函数是访问结点的数据域,其要求视具体问题而定。树采用二叉链表的存储结构,用指针变量T
来指向。
2. 非递归算法
设T
是指向二叉树根结点的指针变量,非递归算法是:
若二叉树为空,则返回;否则,令p=T
;
(1)访问p
所指向的结点;
(2)q=p->Rchild
,若q
不为空,则q
进栈;
(3)p=p->Lchild
,若p
不为空,转(1),否则转(4);
(4) 退栈到p
,转(1),直到栈空为止。
#define MAX_NODE 50
void PreorderTraverse(BTNode *T)
{
BTNode *Stack[MAX_NODE], *p = T, *q;
int top = 0;
if (T == NULL)
printf(“ Binary Tree is Empty !\n”);
else
{
do
{
visit(p->data);
q = p->Rchild;
if (q != NULL)
stack[++top] = q;
p = p->Lchild;
if (p == NULL)
{
p = stack[top];
top--;
}
} while (p != NULL);
}
}
6.3.2 中序遍历二叉树
1. 递归算法
算法的递归定义是:
若二叉树为空,则遍历结束;否则
⑴ 中序遍历左子树(递归调用本算法);
⑵ 访问根结点;
⑶ 中序遍历右子树(递归调用本算法)。
void InorderTraverse(BTNode *T)
{
if (T != NULL)
{
InorderTraverse(T->Lchild);
visit(T->data); /* 访问根结点 */
InorderTraverse(T->Rchild);
}
}
2. 非递归算法
设T是指向二叉树根结点的指针变量,非递归算法是:
若二叉树为空,则返回;否则,令p=T
(1) 若p
不为空,p
进栈, p=p->Lchild
;
(2)否则(即p
为空),退栈到p
,访问p
所指向的结点;
(3) p=p->Rchild
,转(1);
直到栈空为止。
#define MAX_NODE 50
void InorderTraverse(BTNode *T)
{
BTNode *Stack[MAX_NODE], *p = T;
int top = 0, bool = 1;
if (T == NULL)
printf(“ Binary Tree is Empty !\n”);
else
{
do
{
while (p != NULL)//一直入栈到最左侧的叶子结点
{
stack[++top] = p;
p = p->Lchild;
}
if (top == 0)
bool = 0;
else
{
p = stack[top];
top--;
visit(p->data);
p = p->Rchild;
}
} while (bool != 0);
}
}
6.3.3 后序遍历二叉树
如图6-9所示的二叉树表示表达式:
(
a
+
b
∗
(
c
−
d
)
−
e
/
f
)
(a+b*(c-d)-e/f)
(a+b∗(c−d)−e/f)
按不同的次序遍历此二叉树,将访问的结点按先后次序排列起来的次序是:
其先序序列为:
−
+
a
∗
b
−
c
d
/
e
f
-+a*b-cd/ef
−+a∗b−cd/ef
其中序序列为:
a
+
b
∗
c
−
d
−
e
/
f
a+b*c-d-e/f
a+b∗c−d−e/f
其后序序列为:
a
b
c
d
−
∗
+
e
f
/
−
abcd-*+ef/-
abcd−∗+ef/−