目录
树型结构是以分支关系定义的层次结构,它是一种重要的非线性结构。
树型结构在客观世界中广泛存在,例如人类的家庭族谱以及各种社会组织机构都可以用树形结构来表示,又如在计算机文件管理和信息组织方面也用到树形结构。
树( tree )是由一个或多个结点组成的有限集合T。
其中:
(1)一个特定的结点称为该树的根(root)结点 ;
(2)结点之外的其余结点可分为m(m ≥ 0)个互不相交的有限集合 T1 ,T2 ,......,Tm ,且其中每一个集合本身又是一棵树,称之为根的子树(subtree)。
注意:
树的递归定义刻画了树的固有特性:一棵非空树是由若干棵子树构成的,而子树又可由若干棵更小的子树构成。
注意:
(1)树的根结点没有前驱结点,除根结点之外的所有结点有且只有一个前驱结点。
(2)树中所有结点可以有零个或多个后继结点。

在图 6.1 中是一棵由 11 个结点组成树 T 。其中 A 是根结点,其余结点分为三个互不相交的子集:T1 ={B,E,F},T2 ={C,G},T3 ={D,H,I,J,K}。T1 ,T2 ,T3 都是树根A的子树,这三棵子树的根结点分别是B,C,D 。每棵子树本身也是一棵树,可继续划分。例如子树T3以D为根结点,它的其余结点又可分为三个互相交的子集T31 ={H},T32 ={I,K},T33 ={J},而其中T31,T33可都认为是仅有一个根结点的子树。
树形图表示(树结构的主要表示方法)
树的树形图表示中:结点用圆圈表示,结点的名字写在圆圈旁边(有时亦可写在圆圈内)。

用该定义来分析上图所示的树:
图中的树由结点的有限集T={A,B,C,D,E,F,C,H,I,J}所构成,其中A是根结点,T中其余结点可分成三个互不相交的子集:
T1={B,E,F,I,J}, T2={C}, T3={D,G,H}。
T1、T2和T3是根A的三棵子树,且本身又都是一棵树。例如T1,其根为B,其余结点可分为两个互不相交的的子集T11={E}和T12={F,I,J},它们都是B的子树。显然T11是只含一个根结点E的树,而T12的根F又有两棵互不相交的子树{I}和{J},其本身又都是只含一个根结点的树。

二叉树是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树的形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。
二叉树(BinaryTree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的、分别称作这个根的左子树和右子树的二叉树组成。
这个定义是递归的。由于左、右子树也是二叉树, 因此子树也可为空树。图 6.3 中展现了五种不同基本形态的二叉树。

其中 (a) 为空树, (b) 为仅有一个结点的二叉树, (c) 是仅有左子树而右子树为空的二叉树, (d) 是仅有右子树而左子树为空的二叉树, (e) 是左、右子树均非空的二叉树。这里应特别注意的是,二叉树的左子树和右子树是严格区分并且不能随意颠倒的,图 6.3(c) 与图 6.3(d) 就是两棵不同的二叉树。
二叉树具有以下重要性质:

2、完全二叉树(Complete BinaryTree)
若一棵二叉树至多只有最下面的两层上结点的度数可以小于2,并且最下一层上的结点都集中在该层最左边的若干位置上,则此二叉树称为完全二叉树。 完全二叉树的特点:
(1)满二叉树是完全二叉树,完全二叉树不一定是满二叉树。
(2)在满二叉树的最下一层上,从最右边开始连续删去若干结点后得到的二叉树仍然是一棵完全二叉树。
(3)在完全二叉树中,若某个结点没有左孩子,则它一定没有右孩子,即该结点必是叶结点。

二叉树的存储结构有多种,最常用的有两种:是顺序存储结构和链表存储结构。

优点和缺点
① 对完全二叉树而言,顺序存储结构既简单又节省存储空间。
② 一般的二叉树采用顺序存储结构时,虽然简单,但易造成存储空间的浪费。如:最坏的情况下,一个深度为k且只有k个结点的右单支树需要2k-1个结点的存储空间。
③ 在对顺序存储的二叉树做插入和删除结点操作时,要大量移动结点。
typedef int DATATYPE
typedef struct tagBTNODE
{
DATATYPE data; /* 数据域 */
struct tagBTNODE lchild; / 左指针域 */
struct tagBTNODE rchild; / 左指针域 */
}BTNODE;

对于如图 6.6(a) 中的二叉树T ,它的二叉链表存储结构如图6.6(b) 。 二叉树的遍历
所谓二叉树的遍历是指按某条搜索路径访问树中的每一个结点,使得每一个结点均被访问一次并且仅被访问一次。
从根结点出发,逆时针沿着二叉树外缘移动,对每个结点均途径三次,最后回到根结点。

N(Node)、L(Left subtree)和R(Right subtree)
(1)先根遍历(NLR):根左右
如果二叉树为空,则空操作。否则依次执行以下三个操作:
1>. 访问跟结点
2>. 先根遍历左子树
3>. 先根遍历右子树
算法实现如下:
void PreOrder(BTNODE *root)
{
if(root != NULL)
{
printf(“%d “, root ->data);
PreOrder(root ->lchild);
PreOrder(root ->rchild);
}
}
遍历结果如下:
A B D C E F
(2)中根遍历(LNR):左根右
如果二叉树为空,则空操作。否则依次执行以下三个操作:
1>. 中根遍历左子树
2>. 访问根节点
3>. 中根遍历右子树
算法实现如下:
void InOrder(BTNODE *root)
{
if(root != NULL)
{
InOrder(root ->lchild);
printf(“%d “, root ->data);
InOrder(root ->rchild);
}
}
遍历结果如下:
D B A E C F
(3) 后根遍历(LRN):左右根
如果二叉树为空,则空操作。否则依次执行以下三个操作:
1>. 后跟遍历左子树
2>. 后根遍历右子树
3>. 访问根节点
算法实现如下:
void PostOrder(BTNODE *root)
{
if(root != NULL)
{
PostOrder(root ->lchild);
PostOrder(root ->rchild);
printf(“%d “, root ->data);
}
}
遍历结果如下:
D B E F C A
1、

2、

3、
