1. 定义
1.1 树
文字
树是n(n>=0)个结点的有限集,它或为空树(n=0),或为非空树,对于非空树T:
(1) 有且仅有一个称之为根的结点
(2) 除根节点以外的其余节点可分为m个互不相交的有限集T1,T2, …, Tm,其中每个集合又是一棵树,称为根的子树抽象数据类型
ADT Tree{
数据对象D: D是具有相同特性的数据元素的集合
数据关系R: 若D为空集,则称为空树;
若D仅含一个数据元素,则R为空集,否则R={H},H是如下二元关系:
(1) 在D中存在唯一的称为根的数据元素root,它在关系H下无前驱;
(2) 若D-{root}≠∅,则存在D-{root}的一个元素划分D1, D2, …,Dm(m>0),
对任意j≠k(1<=j,k<=m)有Dj∩Dk=∅,且对任意的i(1<=i<=m),
唯一存在数据元素xi∈Di,有∈H;
(3) 对应于D-{root}的划分,H-{, …, }
有唯一的一个划分H1, H2, …, Hm(m>0),对任意的j≠k(1<=j,k<=m)
有Hj∩Hk=∅, 且对任意的i(1<=i<=m),Hi是Di上的二元关系,
(Di, {Hi})是一棵符合本定义的树,称为根root的子树
基本操作P:
构造空树T
销毁树T
按definition构造树T
将树T清空
判断树T是否非空
返回树T的深度
返回T的根
返回树某个结点的值
给树的某个结点赋值
返回结点的双亲
返回结点的最左孩子
返回结点的右兄弟
插入c为T中p指结点的第i棵子树
删除T中p所指结点的第i课子树
按某种次序对T中每个结点访问一次
}
1.2 二叉树
文字
(1) 有且仅有一个称之为根的结点
(2) 除根节点以外的其余节点分为两个互不相交的子集T1, T2,分别称为T的左子树和右子树,且T1和T2本身又是二叉树二叉树不存在度大于2的结点二叉树有左右之分
抽象数据类型
满二叉树和完全二叉树
满二叉树:深度为k,节点数为2k-1
完全二叉树:编号和满二叉树一样(叶子结点只能在倒数两层出现)
2. 二叉树的性质和存储结构
2.1 二叉树的性质
在二叉树的第i层上至多有2i-1个结点(i>=1)
深度为k的二叉树至多有2K-1个结点(k>=1)
对任何一棵二叉树T,如果其终端节点数为n0,度为2的结点数为n2,则n0=n2+1
具有n个结点的完全二叉树的深度为[log2n]+1
一棵具有n个结点的完全二叉树,对于任一结点i(1<=i<=n),有
(1) 如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲PARENT(i)是结点[i/2]
(2) 如果2i>n,则i无左孩子(叶子结点);否则左孩子是2i
(3) 如果2i+1>n,则i无右孩子,否则右孩子是2i+1
2.2 二叉树的存储结构
顺序存储
完全二叉树可以按层序存储,一般二叉树为空的元素用其它标志填上
造成空间浪费
//------------1. 顺序存储结构-----------
链式存储
二叉树的结点由一个数据元素和分别指向其两个左右子树的分支构成,则表示二叉树的链表中至少包含3个域,数据域和左、右指针域(二叉链表)。有时为了方便会在结点结构中增加一个指向其双亲结点的在指针域(三叉链表)。n个结点的二叉链表中含有n+1个空链域。
//------------2. 二叉树的二叉链表存储表示-----------
3. 二叉树的重要算法
3.1 二叉树的遍历
先序遍历
根左右
void
中序遍历
左根右
(1) 递归算法
void
(2) 非递归算法
void
后序遍历
左右根
void
根据中序遍历和先/后序可以唯一确定一棵二叉树
ex: 中序遍历和先序遍历序列
由先序遍历序列知道根节点,然后由中序遍历分出它的左右子树,依次往复直到每个元素都排列在树上其它操作的算法
(1) 根据先序遍历的序列建立一棵二叉树(空子树用“#”表示)
void
(2) 复制二叉树
void
(3) 计算二叉树的深度
int
(4) 统计二叉树中结点的个数
int
3.2 线索二叉树
3.2.1 线索化
(1) 遍历二叉树得到的是一个线性序列,每个结点仅有一个直接前驱和直接后继。由于一个有n个结点的二叉树有n+1个空链域,故可以用这些空链域存储结点的直接前驱和直接后继的信息。
//-----------------二叉树的线索存储表示-----------------
(2) 以结点p为根的子树中序线索化
void
(3) 带头结点的二叉树中序线索化
头结点的lchild指向根节点,rchild指向最后一个结点,第一个结点lchild指向头结点,最后一个结点的rchild指向头结点,建立双向线索链表
void
3.2.2 线索化二叉树遍历
void
4. 树和森林
4.1 树的存储结构
双亲表示法
//树的双亲表示
孩子表示法
树中每个结点含有多个指针域,用多重链表,每个指针指向一棵子树的根节点
(3) 复合链表结构
//树的孩子表示
孩子兄弟表示法
//树的孩子兄弟表示
树和二叉树的转换
可以用二叉链表表示树,即,普通树和二叉树之间可以唯一转换
二叉树变成树:去掉每一个指向右子树的箭头,依次把该节点的右子树和它的双亲连起来
树变成二叉树:去掉和其他孩子的箭头,依次连上右兄弟的结点
于是普通树可以用一棵根节点只有左子树的二叉树表示
4.2 森林和二叉树转换
森林转换为二叉树
把森林里面的每棵树转化为二叉树,再用根节点的右子树把它们依次相连二叉树转换为森林
把根节点和它的右子树依次断开,得到若干棵只有左子树的二叉树
再把每棵二叉树转换为树
4.3 树和森林的遍历
树的遍历
(1) 先根遍历: 先访问每个树的根节点,再遍历子树
(2) 后根遍历: 先遍历每个子树,再访问根节点森林的遍历
(1) 先序遍历:访问第一棵树根节点
先序遍历第一棵树的根节点的子树森林
先序遍历除去第一棵树之后剩余的树构成的森林(2) 中序遍历
中序遍历第一棵树根节点的子树森林
访问第一课树的根节点
中序遍历除了第一棵树之后剩余的树构成的森林
5. 哈夫曼树
5.1 基本概念
路径
树中一个结点到另一个结点间的分支所构成路径长度
路径上的分支数目带权路径长度
结点到根的路径长度与结点上权的乘积树的路径长度
从树根到每一个结点的路径长度之和树的带权路径长度
树中所有叶子结点的带权路径长度之和哈夫曼树
带权路径长最小的树
5.2 哈夫曼树的构造过程
根据给定的n个权值{w1, w2, …, wn},构造n棵只有结点的二叉树
在森林中选取两棵根节点权值最小的树作左右子树,构造新的二叉树,新二叉树的根结点权值为左右子树根结点权值之和
在森林中删除原来的两棵子树,同时将新得到的二叉树加入森林中
重复上述两步,直到只含一棵树为止,这棵树即哈夫曼树
注意:一个具有n个叶子结点的哈夫曼树共有2n-1个结点
Select
5.3 应用实例
5.3.1 使得多个else-if语句平均判决次数最少
5.3.2 哈夫曼编码
关键:
(1) 出现次数较多的字符尽可能采用短的编码
(2) 任一字符的编码都不是另一个字符的编码前缀
使用二叉树设计前缀编码,左分支用0,右分支用1注意
(1) 哈夫曼编码保证是前缀编码
没有一片树叶是另一片树叶的祖先
(2) 字符总长最短
哈夫曼树带权路径长度最短,字符总长最短构造哈夫曼编码
typedef