一、二叉树
1.1 树
考虑元素的有限集合,该集合或为空,或使其中一个元素为根【root】,其余的元素组成了新的非空有限元素的集合,这种集合结构类型称为树。一棵树除了根以外的集合构成了树的子树【subtree】。
结点具有的子树的个数称为结点的度,树中结点的度的最大值称为树的度。若结点的度为0,则该节点是一个叶子结点,否则为分支结点。
树中某个结点的子树的根称为该结点的子结点,该结点是其子结点的父结点。具有同一个父节点的子结点互为兄弟节点。
若结点
n
i
n_i
ni是结点
n
i
+
1
n_{i+1}
ni+1的父节点,则把
n
1
,
.
.
.
n
k
n_1, ...n_k
n1,...nk称为
n
1
n_1
n1到
n
k
n_k
nk的路径,其经过的边的个数称为路径长度。
在树中,如果有一条路径从结点
x
x
x通过子结点到达结点
y
y
y,则
x
x
x是
y
y
y的祖先结点,而
y
y
y是
x
x
x的子孙结点。
树的级,也称层数描述了父子结点的层次关系,定义根节点的级为1且某个节点的子结点的级比该结点高1;树中所有节点中,最大的级称为树的高度。
若树的子结点是从左到右有序的,称其为有序树,否则为无序树。
由互不相交的树组成的集合称为森林。
如果两个树的结构相同,称为相似二叉树;若相似二叉树的对应信息也相同,称为等价二叉树。
1.2 二叉树
如果一棵树或为空,或其所有的结点都至多有两颗子树,分别为左子树与右子树,该类型的树称为二叉树【Binary Tree】。
二叉树的度不大于2,且子树有序,即使某个节点仅有一棵子树,也有左右之分。
当一棵二叉树的所有结点都只有左子树,称其为左斜树;反之,称其为右斜树。
若一颗二叉树的所有分支结点都有两颗子树,且叶子结点都在最后一层,称其为满二叉树。高度为
K
K
K的满二叉树的结点个数为
2
K
−
1
2^K-1
2K−1。
对于高度相同的树与满二叉树,将其自顶向下,同一层自左向右的顺序编号,若树的编号与满二叉树的编号完全相同,且叶均在最后两层,那么称该树为完全二叉树。
完全二叉树的顺序储存结构为自顶向下,同一层自左向右的顺序连续编号。
1.3 二叉树的链式描述
二叉树的链式描述中,二叉树的结点存放数据信息外,还设置两个指示左右子树的指针,并用一个指向根节点的指针标识这个二叉树,形成二叉链。其存储结构定义为
struct node{
struct node *lchild;
struct node *rchild;
datatype data;
};
typedef struct node *BTree;
此外,如果在链表描述的基础上增加指向父结点的指针,则形成了动态三叉链表。
1.4 二叉树的遍历
按照某种策略,按照一定的次序访问二叉树中的每一个结点,使每个结点被访问且仅被访问一次,该过程称为二叉树的遍历。遍历使得树的非线性结构线性化,得到二叉树结点的线性序列。
遍历的策略包括:
-先序遍历,首先访问根,再访问左右子树;
-中序遍历,依次访问左子树、根、右子树;
-后序遍历,首先访问左右子树,再访问根;
-层次遍历,按照自顶向下,同一层自左向右的顺序访问。
二叉树的遍历可以简单的递归,也可以通过栈进行迭代,先序的遍历非递归算法步骤为:
1.初始化栈
s
s
s为空,
r
r
r为指向根节点的指针;
2.若
r
r
r不指向空,则访问
r
r
r->
d
a
t
a
data
data,并将指向
r
r
r的指针压栈,令
r
r
r为指向左子树的指针,并迭代2,直到
r
r
r指向空;
3.若
s
s
s不为空,则弹栈到
r
r
r,并令
r
r
r为指向右子树的指针,并迭代2,直到
s
s
s为空,遍历结束;
此外,层序顺序遍历借助队列,其非递归算法步骤为:
1.初始化队列
q
q
q为空,将树的根指针入队;
2.若
q
q
q不为空,则出队到
r
r
r,访问
r
r
r->
d
a
t
a
data
data,并将
r
r
r的左右子结点依次入队,并迭代2,直到
q
q
q为空。
1.5 线索二叉树
在二叉链表描述的二叉树中,对于有n个节点的二叉树,只有n-1个指向子树的指针,而有n+1个空指针。线索二叉树利用了空指针的空间,将结点在无左子树的情况下指向中序遍历顺序的前驱,无右子树的情况下指向中序遍历顺序的后继,并使用标志位来区分指针指向子结点还是线索,当标志位置1时表示指向子结点。其存储结构定义为
struct node{
struct node *lchild;
struct node *rchild;
datatype data;
bool ltag;
bool rtag;
};
typedef struct node *ThTree;
1.6 堆与选择树
堆【Heap】是一种特殊形式的完全二叉树,当完全二叉树的任意一个结点都不小于其子结点,则称该完全二叉树为最大堆;反之,称为最小堆。
考虑一颗完全二叉树,其有n个外部结点与n-1个内部结点,且每个结点记录了其子结点的某种比较关系,称该类树为选择树。当记录了比较关系的正方,即“胜者”,则成为胜者树,反之称为败者树。选择树可以在
l
o
g
(
n
)
log(n)
log(n)的时间内找到外部结点的最值。
二、树
2.1 一般树的父链描述
由于每个非根结点都有唯一的父节点,故将各节点一般按层序储存在一维数组中,数组的元素为树结点,结点包括其储存的信息与父结点的索引,且根的父结点的索引为0。那么数组的元素结构定义为
struct node{
datatype data;
int parent;
};
而数组从下标1开始按照结点的层序索引对应储存结点。
2.2 一般树的子链描述
将树的所有的结点被组织成线性表,每个结点的所有子结点又依次组织成线性表,使结点指向首个子结点,形成邻接表。
邻接表的元素为结点,邻接表数组的索引与结点对应,且结点包括结点的数据与指向其首个子结点的指针;子结点包括该子结点的索引与指向下一个其兄弟节点的指针。邻接表的元素与表的结构定义为
struct CTnode{
int child;
CTnode *next;
};
struct CTBox{
datatype data;
CTnode *firstchild
};
struct {
CTBox nodes[maxlength];
int n,r;
}CTree;
在上述结构中加入父结点的信息,则构成父子链描述。
2.3 一般树的二叉链描述
二叉树的二叉链存储了左右子树的指针,而为了表示一般树,由于结点的左子结点与右兄弟确定且唯一,那么一般树的二叉链存储左子结点的指针与右兄弟结点的指针。其结构定义为
struct CSnode{
struct CSnode *firstchild;
struct CSnode *rightsib;
datatype data;
};
typedef struct CSnode *CSTree;
因此,一般树与二叉树之间存在着映射关系。
2.4 一般树的遍历操作
一般树与二叉树的遍历策略相似,包括:
-先序遍历,首先访问根,再按从左到右的顺序访问子树;
-后序遍历,首先按从左到右的顺序访问子树,再访问根;
-层次遍历,按照自顶向下,同一层自左向右的顺序访问。
2.5 森林与二叉树
如果将森林中的数棵树看作兄弟结点,那么森林与二叉树之间存在着映射关系,并且任何一个森林对应着唯一的一棵二叉树,反之亦然。
考虑森林
F
=
{
T
1
,
.
.
.
T
n
}
F = \{T_1, ... Tn\}
F={T1,...Tn}与其对应的二叉树
B
(
F
)
B(F)
B(F),那么非空森林转换成二叉树的递归算法步骤为:
1.
B
(
F
)
B(F)
B(F)的根为
T
1
T_1
T1的根;
2.
B
(
F
)
B(F)
B(F)的左子树是
T
1
T_1
T1的子树森林,递归
T
1
T_1
T1的子树森林转换成
B
(
F
)
B(F)
B(F)的二叉树作为左子树;
3.
B
(
F
)
B(F)
B(F)的右子树是
F
F
F的
T
1
T_1
T1之外的树森林,递归
F
F
F的
T
1
T_1
T1之外的树森林转换成
B
(
F
)
B(F)
B(F)的二叉树作为右子树。
非空二叉树转换成森林的算法类似。
森林的遍历策略包括:
-先根遍历,首先访问第一棵树的根,然后访问该树子森林,最后按先根遍历访问其余子树森林;
-后根遍历,首先访问第一棵树的子树,然后访问树的根,最后按后根遍历访问其余子树森林;
三、树的应用
3.1 集合树
假设集合的元素为
1
,
2
,
.
.
.
n
1,2, ... n
1,2,...n,其组成的集合互不相交,在这种结构中定义如下操作:
-
U
n
i
o
n
(
S
i
,
S
j
)
Union(S_i, S_j)
Union(Si,Sj),作集合的交集;
-
F
i
n
d
(
i
)
Find(i)
Find(i),查找包含
i
i
i的集合;
-
I
n
i
t
i
a
l
(
A
,
x
)
Initial(A, x)
Initial(A,x),建立集合
A
=
{
x
}
A = \{x\}
A={x}。
在互不相交的集合定义如上操作,构成了一种特殊的数据类型,称为MFSET,使用树结构可以实现该数据结构。
3.2 判定树
考虑这样的问题:假定有八枚硬币,已知其中一枚是伪造的,假币的重量与真币不同,使用最少的比较次数挑出假币。通过建立树结构的判定手段,可以在3次比较中得到结果。
3.3 最优二叉树
考虑在二叉树中,对于每个结点,如果出现了空子树,则为其增加一个外结点,而原二叉树的结点称为内结点,从而得到了新的二叉树,称为扩充二叉树。
假设扩充二叉树的外结点
j
j
j对应了一个权重
w
j
w_j
wj,外结点到根的距离为
l
j
l_j
lj,那么称
w
j
l
j
w_jl_j
wjlj为结点
j
j
j的加权路长,并令扩充二叉树的加权路长为
W
P
L
=
∑
w
j
l
j
WPL = \sum w_jl_j
WPL=∑wjlj。那么对于给定权重集合为
{
w
1
,
.
.
.
,
w
n
}
\{w_1, ..., w_n\}
{w1,...,wn}的有n个叶节点所构成的所有扩充二叉树中,
W
P
L
WPL
WPL最小的称为最优二叉树,也称为哈夫曼【Huffman】树。
哈夫曼树权值越大的叶结点越靠近根,且不会存在度为1的结点。