Professional Documents
Culture Documents
ch5 树和二叉树
ch5 树和二叉树
教学内容
5.1 树和二叉树的定义
5.2 案例引入
5.3 树和二叉树的抽象数据类型定义
5.4 二叉树的性质和存储结构
5.5 遍历二叉树和线索二叉树
5.6 树和森林
5.7 哈夫曼树及其应用
5.8 案例分析与实现
5.9 小结
教学目标
掌握二叉树的基本概念、性质和存储结构
熟练掌握二叉树的前、中、后序遍历方法
了解线索化二叉树的思想
熟练掌握:哈夫曼树的实现方法、构造哈夫
曼编码的方法
掌握:森林与二叉树的转换,树的遍历方法
5.1 树和二叉树的定义
5.1.1 树的定义
5.1.2 树的基本术语
5.1.3 二叉树的定义
5.1.1 树的定义
定义 树是由 n(n>=0) 个结点组成的有限
集, n=0 时为空树,且对于非空树:
有且仅有一个特定的称为根的结点;
当 n>1 时,其余结点可分为 m(m>0) 个互不相交
的有限集 T1,T2,…,Tm ,其中每一个集合本身又是
一棵树,称为根的子树 (subtree) 。
这里使用了递归定义。树中的每个结点都是
某一子树的根。
结点包含数据项和指向其它结点的分支信息。
5.1.1 树的定义
通常将树根画在顶层,再逐层画出子树,这
与自然界生长的树正好相反。
只有根结点的树
有子树的树 根
A
A
B C D
E F G H I J
子树
K L M
5.1.1 树的定义
例:
A A A
B C B C B E C
D E F G D F G D F G
H I
(a) 一棵树结构 (b) 一个非树结构 (c
一个非树结构
5.1.1 树的定义
树的应用举例——文件结构
My Computer
C: D: E: etc
…… ……
…… …… …… ……
5.1.1 树的定义
树的表示法有几种:
图形表示法
嵌套集合表示法
广义表表示法 这些表示法的示意图
参见教材 P112
凹入 ( 目录 ) 表示法
5.1.1 树的定义
图形表示法: 河南大学 根
教师 学生 其他人员
子树
2016 级 2017 级2018 级 2019 级
叶子
5.1.1 树的定义
树的表示法
A
广义表表示法
B C D
E F G H I J
K L M
( A ( B ( E ( K, L ), F ), C ( G ), D ( H ( M ), I, J ) )
根作为由子树森林组成的表的名字写在表的左边
5.1.2 树的基本术语
基本术语
结点:一个数据元素 + 若干指向子树的分支
结点拥有的子树数(或分支数)称为结点的度。
结点 A 的度是 3 , C 的是 1 , G 的是零。度为零的结
点称为叶子结点 / 终端结点。 K , L , F , G , M ,
I , J 是叶子结点。度不为零的结点称为分支结点 / 非
终端结点。
结点的子树的根称为该结点的孩子,该结点称为
孩子的双亲
D 的孩子是 H , I 和 J ; D 的双亲是 A 。
同一个双亲的孩子是兄弟。
H , I 和 J 是兄弟。
双亲在同一层的结点称为堂兄弟
5.1.2 树的基本术语
基本术语
结点的祖先是从根到该结点所经过的所有结点。
M 的祖先是 A 、 D 和 H 。
结点的子孙是该结点下层子树中的任意结点。
B 的子孙为 E 、 F 、 K 和 L 。
结点的层次
从根到该结点的层数(根结点为第一层)。
树的深度
指所有结点最大的层数( Max{ 各结点的层数 } )。
树的度
所有结点度的最大值( Max{ 各结点的度 } )。
上图中树的度为 3
5.1.2 树的基本术语
基本术语
有序树
结点各子树从左至右有序,不能互换(左为第一)
家谱
无序树
结点各子树可互换位置
机构
森林
指 m 棵互不相交的树的集合
任何一棵非空树是一个二元组 Tree
= ( root , F )
root 被称为根结点, F 被称为子树森林
树结构和线性结构的比较
线性结构 树结构
第一个数据元素 根结点(只有一个)
无前驱 无双亲
最后一个数据元素 叶子结点 ( 可以有多
个)
无后继 无孩子
其它数据元素 其它结点
一个前驱 , 一个后 一个双亲 , 多个孩子
继 一对一
一对多
5.1.3 二叉树的定义
二叉树的概念
n (n≥0) 个结点的有限集合。当 n=0 时称为空
树。
在一棵非空树 T 中:
有一个特定的称为根的结点;
除根结点之外的其余结点被分成两个互不相交的有限
集合 T1 和 T2 ,且 T1 和 T2 都是二叉树,并分别称之为
根的左子树和右子树。
二叉树或为空树;或是由一个
根结点加上两棵分别称为左子树和右
子树的、互不相交的二叉树组成。
右子树
根结点 A
B E
C F
D G
左子树
H K
5.1.3 二叉树的定义
二叉树的基本特点
结点的度小于等于 2
二叉树的子树有左右之分,其次序不能颠倒
二叉树的五种不同形态
5.1.3 二叉树的定义
练习
具有 3 个结点的二叉树可能有几种不同形态?普
通树呢? 5 种 /2 种
5.1.3 二叉树的定义
为何要重点研究每结点最多只有两个 “叉”
的树?
二叉树的结构最简单,规律性最强;
可以证明,所有树都能转为唯一对应的二叉树,
不失一般性。
普通树(多叉树)若不转化为二叉树,则运算很
难实现。
5.2 案例引入
案例 5.1 :数据压缩问题
将数据文件转换成由 0 、 1 组成的
二进制串,称之为编码。
( a )等长编码方案 ( b )不等长编码方案 1 ( c )不等长
编码方案 2
字符 编码 字符 编码 字符 编码
a 00 a 0 a 0
b 01 b 10 b 01
c 10 c 110 c 010
d 11 d 111 d 111
5.3 树和二叉树的抽象数据类型定义
二叉树的抽象数据类型定义( P115 )
ADT BinaryTree{
数据对象 D: D 是具有相同特性的数据元素的集合。
数据关系 R:
5.3 树和二叉树的抽象数据类型定义
CreateBiTree(&T,definition)
初始条件; definition 给出二叉树 T 的定义。
操作结果:按 definition 构造二叉树 T 。
PreOrderTraverse(T)
初始条件:二叉树 T 存在。
操作结果:先序遍历 T ,对每个结点访问一次。
InOrderTraverse(T)
初始条件:二叉树 T 存在。
操作结果:中序遍历 T ,对每个结点访问一次。
PostOrderTraverse(T)
初始条件:二叉树 T 存在。
操作结果:后序遍历 T ,对每个结点访问一次。
5.4 二叉树的性质和存储结构
5.4.1 二叉树的性质
5.4.2 二叉树的存储结构
5.4.1 二叉树的性质
二叉树的性质
性质 1
在二叉树的第 i 层上至多有 2i - 1 个结点
第 i 层上至少有 1 个结点
性质 2
深度为 k 的二叉树至多有 2k-1 个结点
深度为 k 的二叉树至少有 k 个结点
性质 3
对于任何一棵二叉树 T ,如果其叶子结点数为 n0 ,度
为 2 的结点数为 n2 ,则 n0=n2+1 。
5.4.1 二叉树的性质
1 1
2 3 2 3
4 5 6 7 4 5 6 7
8 9 10 11 12 8 9 10 11 12
B = n-1 B = 2n2+n1
n=2n2+n1+1 = n2+n1+n0
n2+1 = n0
5.4.1 二叉树的性质
特殊形态的二叉树 只有最后一层叶子不满,且
满二叉树 叶子全部集中在左边
一棵深度为 k 且有 2k -1 个结点的二叉树。
完全二叉树
深度为 k 的,有 n 个结点的二叉树,当且仅当其每一
个结点都与深度为 k 的满二叉树中编号从 1 至 n 的结
点一一对应。
1 1
2 3 2 3
4 5 6 7 4 5 6 7
8 9 10 11 12 13 14 15 8 9 10 11 12
5.4.1 二叉树的性质 1
2 3
满二叉树的特点
4 5 6 7
叶子只能出现在最下一层
只有度为 0 和度为 2 的结点 8 9 10 11 12 13 14 15
在同样深度的二叉树中叶子结点(结点)个数最多
完全二叉树的特点
叶子结点只能出现在最下两层,且最下层的叶子结点都集
中在二叉树的左部;
完全二叉树中如果有度为 1 的结点,只可能有一个,且该
结点只有左孩子。 1
8 9 10 11 12
5.4.1 二叉树的性质
满二叉树和完全二叉树的区别
满二叉树是叶子一个也不少的树,而完全二叉树
虽然前 n-1 层是满的,但最底层却允许在右边缺
少连续若干个结点。满二叉树是完全二叉树的一
个特例。
完全二叉树
5.4.1 二叉树的性质
性质 4
具有 n 个结点的完全二叉树的深度为 log 2 n +1
。 1
k 1 k
2 n 2
2 3
4 5 6 7 k-1 2k 1 1
层
8 9 10 11 12 k层 2k 1
k 1
2 n k log 2n 1
n n 2k k log 2n
log2n log 2n log 2n 1
5.4.1 二叉树的性质
性质 5
对于完全二叉树,若从上至下、从左至右编号,
则编号为 i 的结点,其左孩子编号必为 2i ,右孩
子编号必为 2i + 1 ,双亲的编号必为 i/2 。
性质 5 表明,在完全二叉树中,结点的层序编号反
映了结点之间的逻辑关系。
课堂练习
一棵完全二叉树有 5000 个结点,则其叶子结点
的个数是 2500 。
5.4.1 二叉树的性质
树T中各结点的度的最大值称为树T的 。
A. 高度 B. 层次 C. 深度 D. 度
深度为 k 的二叉树的结点总数,最多为 个。
A.2k-1 B. log2k C. 2k - 1 D.2k
深度为 9 的二叉树中至少有 个结点。
A. 29 B. 28 C. 9 D. 29-1
一棵完全二叉树具有 1000 个结点,则它有 500 个叶子
结点,有 499 1
个度为 2 的结点,有 个结点
只有非空左子树,有
0 个结点只有非空右子树。
5.4.2 二叉树的存储结构
二叉树的存储结构
顺序存储结构
链式存储结构
5.4.2 二叉树的存储结构
二叉树的顺序存储
实现:按满二叉树的结点层次编号,依次存
放二叉树中的数据元素
特点:
结点间关系蕴含在其存储位置中
浪费空间,适于存满二叉树和完全二叉树
5.4.2 二叉树的存储结构
二叉树的顺序存储
0 1 2 3 4 5 6 7 8 9 10
a b c d e 0 0 0 0 f g
d e
f g
单支树
5.4.2 二叉树的存储结构
二叉树的链式存储结构
用链表来表示一棵二叉树。链表中每个结点由三
个域组成,除了数据域外,还有两个指针域,分
别用来给出该结点的左孩子和右孩子所在结点的
typedef struct BiTNode
存储地址。 { TElemType data;
struct BiTNode *lchild, *rchild;
lchild data rchild } BiTNode , *BiTree;
A
B
data
C D
E F
lchild rchild
G
练习
画出该二叉树的二叉链表
5.4.2 二叉树的存储结构
三叉链表
typedef struct TriTNode lchild data parent rchild
{ datatype data;
struct TriTNode *lchild, *rchild, *parent;
}TriTree;
A ^ ^
A
B B
C D D
^ C ^
E F
^ E ^ F ^
G
^ G ^
5.5 遍历二叉树与线索二叉树
二叉树的遍历
遍历定义
指以一定的次序访问二叉树中的每个结点,并且每个
结点仅被访问一次。
“ 访问”的含义:输出结点的信息
遍历用途
是树结构插入、删除、修改、查找和排序运算的前
提,是二叉树一切运算的基础和核心。
一次完整的遍历可产生树中结点的一个线性序列
5.5.1 遍历二叉树
遍历规则
二叉树由根、左子树、右子树构成
D 、 L 、 R 的组合定义了六种可能的遍历方
案:
LDR, LRD, DLR, DRL, RDL, RLD
若限定先左后右,则有三种实现方案:
DLR LDR LRD
先序遍历 中序遍历
后序遍历
ROOT
LCHILD RCHILD
先左后右的遍历算法
访问根结点、遍历左子树、遍历右子树
先(根)序的遍历算法
根
中(根)序的遍历算法
左 右
子树 子树
后(根)序的遍历算法
5.5.1 遍历二叉树
先序遍历 : 先根再左再右
口诀:
DLR— 先序遍历,即先根再左再右
LDR— 中序遍历,即先左再根再右
LRD— 后序遍历,即先左再右再根
5.5.1 遍历二叉树
例 2 + 先序遍历
+**/ABCDE
前缀表示
* E
中序遍历
* D A/B*C*D+E
中缀表示
/ C 后序遍历
AB/C*D*E+
A B 后缀表示
层序遍历二叉树,是从根结点开 层序遍历
始遍历,按层次次序“自上而下, +*E*D/CAB
从左至右”访问树中的各结点。
5.5.1 遍历二叉树
例 3 先序序列:
A ABCDEFGHK
B E 中序序列:
C F BDCAEHGKF
D G
后序序列:
H K DCBHKGFEA
6.3 遍历二叉树与线索二叉树
练习 写出二叉树遍历的次序
先序: ADEFGBC
中序: DFEGABC
后序: FGEDCBA
遍历的算法实现-先序遍历
若二叉树为空,则空操作 D L R
否则
访问根结点 (D) A
先序遍历左子树 (L) D L R D L R
先序遍历右子树 (R)
B C
>
>
>
A
D L R
B C
D
>
>
D
先序遍历序列: A B D C
先序遍历算法
void PreOrderTraverse(BiTree T){
if(T) {
cout<<T->data; // 访问根结点
PreOrderTraverse(T->lchild); // 递归遍历左子树
PreOrderTraverse(T->rchild); // 递归遍历右子树
}
}
void PreOrderTraverse(BiTree T){ 先序序列: ABDC A
if(T) {
cout<<T->data;
PreOrderTraverse(T->lchild); 左是空返回 B C
PreOrderTraverse(T->rchild); } 左是空返回
} 右是空返回
T
>
D
返回
左是空返回
T B
T D 右是空返回
T A printf(B); T
>
主程序 printf(D);
printf(A); pre(T L);
pre(T 返回
pre(T L); pre(T R);
L);
pre(T R); T
>
Pre( T )
pre(T R);
T C 返回
T
>
printf(C);
pre(T L); 返回
pre(T R); T
北京林业大学信息学院 返回
9/11/23
>
遍历的算法实现-中序遍历
若二叉树为空,则空操作 L D R
否则 :
中序遍历左子树 (L) A
访问根结点 (D) L D R L D R
中序遍历右子树 (R)
>
A B C
>
>
L D R
B C
D
>
>
D
中序遍历序列: B D A
C
中序遍历算法
void InOrderTraverse(BiTree T){
if(T) {
InOrderTraverse(T->lchild); // 递归遍历左子树
cout<<T->data; // 访问根结点
InOrderTraverse(T->rchild); // 递归遍历右子树
}
}
遍历的算法实现-后序遍历
若二叉树为空,则空操作 L R D
否则
后序遍历左子树 (L)
后序遍历右子树 (R) A
访问根结点 (D) L R D L R D
A
B C
>
>
L R D
B C
D
>
D
>
后序遍历序列: D B C
A
后序遍历算法
void PostOrderTraverse(BiTree T){
if(T) {
PostOrderTraverse(T->lchild); // 递归遍历左子树
PostOrderTraverse(T->rchild); // 递归遍历右子树
cout<<T->data; // 访问根结点
}
}
遍历算法的分析 void InOrderTraverse(BiTree T){
if(T) {
InOrderTraverse(T->lchild);
cout<<T->data;
void PreOrderTraverse(BiTree T){
InOrderTraverse(T->rchild);
if(T) {
}
cout<<T->data;
}
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
} void PostOrderTraverse(BiTree T){
} if(T) {
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
cout<<T->data;
}
}
遍历算法的分析
如果去掉输出语句,从递归的角度看,三种算法的访
问路径是相同的,只是访问结点的时机不同。
从虚线的出发点到终点的路径
A 上,每个结点经过 3 次。
B C 第 1 次经过时访问=先序遍历
第 2 次经过时访问=中序遍历
第 3 次经过时访问=后序遍历
D E
二叉树遍历的时间效率和空间效率
F G
时间效率 :O(n) // 每个结点只访问一次
空间效率 :O(n) // 栈占用的最大辅助空
间
(精确值:树深为 k 的递归遍历需要
k+1 个辅助单元!)
二叉树遍历算法的非递归实现
中序遍历二叉树的非递归算法
D
B
E
CA
中序遍历二叉树的非递归算法
算法步骤
初始化一个空栈 S ,指针 p 指向根结点
声明指针变量 q ,用来存放栈顶弹出的元素
当 p 非空或栈 S 非空时,循环执行以下操作:
如果 p 非空,则将 p 进栈, p 指向该结点的左孩子
如果 p 为空,则弹出栈顶元素并访问,将 p 指向该结
点的右孩子。
中序遍历二叉树的非递归算法
算法描述
void InOrderTraverse ( Bitree T)
{
InitStack( S );
p=T;
BiTree q;
while ( p || !StackEmpty(S) )
{
if ( p){
Push( S, p );
p=p->lchild );// 根指针进栈,遍历左子树
}
else{
Pop( S, q ); // 退栈
cout<<q->data;// 访问根结点
p=q->rchild;// 遍历右子树
}
}
}
二叉树遍历算法的应用举例
例 1 创建一棵二叉树
例 2 计算二叉树结点总数
例 3 计算二叉树中叶子结点个数
例 4 计算二叉树的深度
二叉树遍历算法的应用举例
例 1 创建一棵二叉树
思路:用先序遍历算法创建二叉树
已知先序序列为:
A B C # # D E # G # # F # # # 。
先序遍历的顺序建立二叉链表
算法 5.3 ( P126 )
void CreateBiTree(BiTree &T){
// 按先序次序输入二叉树中结点的值(一个字符),创建二叉链表表示的二叉树
T
char ch;
cin >> ch;
if(ch=='#') T=NULL; // 递归结束,建空树
else
{
T=new BiTNode;
T->data=ch; // 生成根结点
CreateBiTree(T->lchild); // 递归创建左子树
CreateBiTree(T->rchild); // 递归创建右子树
} }
二叉树遍历算法的应用举例
例 2 计算二叉树结点总数
如果是空树,则结点个数为 0 ;
否则,结点个数为左子树的结点个数 + 右子树的
结点个数 +1 。
算法 5.6
int NodeCount(BiTree T){
if(T == NULL )
return 0;
else
return NodeCount(T->lchild)+NodeCount(T-
>rchild)+1;
二叉树遍历算法的应用举例
例 3 计算二叉树中叶子结点个数
如果是空树,则叶子结点个数为 0 ;
如果根节点左右子树为空,则叶子结点数为 1 ;
否则,为左子树的叶子结点个数 + 右子树的叶子
结点个数。
int LeafCount(BiTree T){
if(T==NULL) // 如果是空树返回 0
return 0;
if (T->lchild == NULL && T->rchild == NULL)
return 1; // 如果是叶子结点返回 1
else return LeafCount(T->lchild) + LeafCount(T-
>rchild);
}
二叉树遍历算法的应用举例
例 4 计算二叉树的深度
如果是空树,则深度为 0 ;
否则,递归计算左子树的深度记为 m ,递归计算
右子树的深度记为 n ,二叉树的深度则为 m 与 n
的较大者加 1 。int Depth(BiTree T)
{
if (T==NULL) return 0;
else
{
m=Depth(T->lchild);
n=Depth(T->rchild);
if (m>=n) return m+1;
else return n+1;
}
}
特别讨论:若已知先序 / 后序遍历结果和中序遍历结果,
能否“恢复”出二叉树?
例:已知一棵二叉树的中序序列和后序序列分别是 BDCEAFHG
和 DECBHGFA ,请画出这棵二叉树。
分析:
① 由后序遍历特征,根结点必在后序序列尾部(即 A );
② 由中序遍历特征,根结点必在其中间,而且其左部必全部是
后序遍历: D E C BB H GFF A
A
B F
C G
D E H
(BDCE) ( F(H G
H)
(DCE) G)
若已知一棵二叉树的先序序列和中序序列,
能否唯一确定这棵二叉树呢?怎样确定?
例如 : a b c d e f g 先序序列
c b d a e g f 中序序列
a
b ^ e
^ c ^ ^d ^ f ^
g
^ ^
若已知一棵二叉树的先序序列和后序序列,能否
唯一确定这棵二叉树呢?
A A
B B
C C
重要结论
若二叉树中各结点的值均不相同,则:
由二叉树的先序序列和中序序列,或由其后序序
列和中序序列均能唯一地确定一棵二叉树,
但由先序序列和后序序列却不一定能唯一地确定
一棵二叉树。
练习
已知一棵二叉树结点的先序序列和中序序列分别为:
先序序列: 18 14 7 3 11 22 35 27
中序序列: 3 7 11 14 18 22 27 35
给出该二叉树的树形表示(画出该二叉树)。
18
14 22
7 35
3 11 27
讨论:用二叉链表( l_child, r_child )存储包含 n 个结点
的二叉树,结点的指针域中有多少个空指针? n+1
分析:用二叉链表存储包含 n 个结点的二叉树,必有 2n
个链域(见二叉链表数据类型说明)。
除根结点外,二叉树中每一个结点有且仅有一个双亲
(直接前驱),所以只会有 n-1 个链域存放指向非空子女结点
(即直接后继)的指针。
思考:二叉链表空间效率这么低,能否利用这些空闲区存放
有用的信息或线索?
—— 我们可以用它来存放当前结点在某种遍历序列下的直接前
驱和后继等线索,,以提高遍历效率。
线索二叉树
5.5.2 线索二叉树
对于普通二叉树的某个结点,能够直接得到
的只有它的左右孩子信息,而该结点的直接
前驱和直接后继必须在遍历过程中获得。
若将遍历后对应的有关前驱和后继预存起
来,则从第一个结点开始就能很快“顺藤摸
瓜”而遍历整个树。
例如对于中序遍历结果: C B E G D F A G
除 C 、 G 外,显然每个结点具有唯一前驱和唯一后
继!
5.5.2 线索二叉树
如何保存前驱和后继信息?
增加两个域: fwd 和 bwd ;
两种解决方法
利用空链域( n+1 个空链域)
5.5.2 线索二叉树
线索二叉树的构造
线索二叉树的结点结构如下:
lchild LTag data RTag rchild
LTag 若 LTag=0, lchild 域指向左孩子;
若 LTag=1, lchild 域指向其直接前驱
( 线索 ) 。
RTag 若 RTag=0, rchild 域指向右孩子;
若 RTag=1, rchild 域指向其直接后继
( 线索 ) 。
线索二叉树的结点定义
typedef struct BiThrNode{
TElemType data;
struct BiThrNode *lchild,*rchild;
unsigned char LTag,RTag;
5.5.2 线索二叉树
线索二叉树的相关术语
线索
指向结点前驱或后继的指针
线索链表
以五域线索结点构成的二叉链表
线索二叉树
加上线索的二叉树
线索化
对二叉树以某种次序遍历使其变为线索二叉树的过程
5.5.2 线索二叉树
例:某先序遍历结果如下表所示,请画出对
应的二叉树。
(多带了两个标志!)
LTag 0 0 1 1 1 1 0 1 0 1
data A G E I D J H C F B
RTag 0 0 0 1 0 1 0 1 1 1
A
G H
E D C F
I J B
先序线索二叉树
LTag=0, lchild 域指向左 lchild LTag data RTag rchild
孩子
LTag=1, lchild 域指向其 T
前驱
0 A0
RTag=0, rchild 域指向右
孩子 A
RTag=1, rchild 域指向其
1 B 0 0 D1
后继 B D
C E 1 C 1 1 E 1 ^
先序序列: ABCDE
中序线索二叉树
lchild LTag data RTag rchild
T
A 0 A0
B D
^ 1 B0 0 D1 ^
C E
1 C 1 1 E 1
中序序列: BCAED
后序线索二叉树
lchild LTag data RTag rchild
T
0 A0
A
1 B 0 0 D1
B D
C E ^ 1 C 1 1 E 1
后序序列: CBEDA
练习
画出与二叉树对应的中序线索二叉树
28
NULL 25 33 NULL
40 60 08 54
55
因为中序遍历序列是: 55 40 25 60 28 08
33 54
对应线索树应当按此规律连线,即在原二叉树中添
加虚线。
练习
画出以下二叉树对应的中序线索二叉树。
该二叉树中序遍历结果为 : H, D, I, B, E, A, F, C, G
root A
为避免悬空
悬空?
态,应增设 B C
一个头结点
悬空? D E F G
H I
练习
对应的中序线索二叉树存储结构如图所示:
注:此图中序遍历结果为 : H, D, I, B, E, A, F, C, G
root 0 -- 1
0 A 0
0 B 0 0 C 0
0 D 0 1 E 1 1 F 1 1 G 1
1 H 1 1 I 1
5.5.2 线索二叉树
构造线索二叉树
在遍历过程中修改空指针:
将空的 lchild 改为结点的直接前驱;
将空的 rchild 改为结点的直接后继。
非空指针呢?
仍然指向孩子结点
5.5.2 线索二叉树
构造线索二叉树 : 建立线索链表
分析:建立线索链表,实质上就是将二叉链表中的空
指针改为指向前驱或后继的线索,而前驱或后继的信
息只有在遍历该二叉树时才能得到。
建立二叉链表
遍历二叉树,将空指针改为线索
以结点 p 为根的子树中序线索化 ( 算法 5.7, 见
P130 )
目的:在中序遍历二叉树时修改空指针,添加前驱或后继。
注解:为方便添加结点的前驱或后继,需要设置两个指针:
p 指针→当前结点指针; pre 指针→前驱结点指针。
技巧:当结点 p 的左域为空时,改写它的左域(装入前驱
pre ),而其右域留给它的后继结点改写。
此外,当前结点的指针 p 应当送到前驱结点的空
右域中。
若 p->lchild = NULL, 则 {p->LTag=1;p->lchild =
pre;}
//p 的前驱结点指针 pre 存入其
左空域
若 pre->rchild = NULL, 则 {pre->RTag = 1;pre-
>rchild=p;}
//p 存入其前驱结点 pre 的右空域
5.5.2 线索二叉树
中序线索链表 头指针
已经建立起二叉链表
的建立过程
0 A 0
0 B ∧ 0 0 C 0
0 ∧ D 0 0 ∧ E ∧ 0 0 ∧ F ∧ 0
0 ∧ G ∧ 0
5.5.2 线索二叉树
中序线索链表 头指针
中序遍历二叉链表
的建立过程 p 为正在访问的结点
0 A 0 pre 为刚访问的结点
0 B ∧ 0 0 C 0
10 ∧ D 0 0 ∧ E ∧ 0 0 ∧ F ∧ 0
p
0 ∧ G ∧ 0
5.5.2 线索二叉树
中序线索链表 头指针
中序遍历二叉链表
的建立过程 p 为正在访问的结点
0 A 0 pre 为刚访问的结点
0 B ∧ 0 0 C 0
10 ∧ D 0 0 ∧ E ∧ 0 0 ∧ F ∧ 0
pre
01 ∧ G ∧ 0
p
5.5.2 线索二叉树
头指针
中序线索链表 中序遍历二叉链表
的建立过程 p 为正在访问的结点
0 A 0
pre 为刚访问的结点
0 B ∧ 0 0 C 0
10 ∧ D 0 0 ∧ E ∧ 0 0 ∧ F ∧ 0
01 G ∧ 1
0
pre
5.5.2 线索二叉树
中序线索链表 头指针
中序遍历二叉链表
的建立过程 p 为正在访问的结点
0 A 0 pre 为刚访问的结点
p
0 B ∧ 01 0 C 0
pre
10 ∧ D 0 0 ∧ E ∧ 0 0 ∧ F ∧ 0
01 G 10
5.5.2 线索二叉树
中序线索链表 头指针
中序遍历二叉链表
的建立过程 p 为正在访问的结点
0 A 0 pre 为刚访问的结点
pre
0 B 01 0 C 0
10 ∧ D 0 01 ∧ E ∧ 0 0 ∧ F ∧ 0
p
01 G 10
5.5.2 线索二叉树
中序线索链表 头指针
中序遍历二叉链表
的建立过程 p 为正在访问的结点
0 A 0 pre 为刚访问的结点
0 B 01 0 C 0
p
10 ∧ D 0 01 E ∧ 01 0 ∧ F ∧ 0
pre
01 G 10
5.5.2 线索二叉树
中序线索链表 头指针
中序遍历二叉链表
的建立过程 p 为正在访问的结点
0 A 0 pre 为刚访问的结点
0 B 01 0 C 0
pre
10 ∧ D 0 01 E 01 01 ∧ F ∧ 0
p
01 G 10
5.5.2 线索二叉树
中序线索链表 头指针
中序遍历二叉链表
的建立过程 p 为正在访问的结点
0 A 0 pre 为刚访问的结点
0 B 01 0 C 0
10 ∧ D 0 01 E 01 01 F ∧ 01
pre
01 G 10
以结点 p 为根的子树中序线索化
void InThreading (BiThrTree p)
//pre 是全局变量 , 初始化时其右孩子指针为空
{ if (p)
{ InThreading( p->lchild ); // 左子树线索化
if ( !p->lchild )
{ p->LTag=1; p->lchild=pre; } // 前驱线索
if ( !pre->rchild )
{ pre->RTag=1; pre->rchild=p; } // 后继线索
pre = p; // 保持 pre 指向 p 的前驱
InThreading(p->rchild); // 右子树线索化
}
}
void InorderThreading(BiThrTree & Thrt, BiThrTree T)
{ // 中序遍历二叉树 T, 并将其中序线索化 , Thrt 指向头结点 .
Thrt =new BiThrNode; // 建头结点
Thrt ->LTag = 0;
Thrt ->RTag = 1;
Thrt ->rchild = Thrt ; // 右指针回指
if ( !T ) Thrt ->lchild = Thrt ; // 若二叉树空 , 则左指针回指
else {
Thrt ->lchild = T; pre = Thrt; // 将头结点与树相连
InThreading(T); // 中序遍历进行中序线索化
pre ->rchild = Thrt;
pre ->RTag = 1; // 最后一个结点线索化
Thrt ->rchild = pre;
}
}
遍历中序线索二叉树
算法思想
从根结点出发沿左指针向下,到达最左下结点 *p,
它是中序遍历的第一个结点,访问 *p
反复查找当前结点 *p 的后继结点,直至遍历结束
若 p->RTag==1, 则其后继结点的指针为 p->rchild;
否则,其后继为结点 *p 的右子树的最左下结点;访问
找到这个后继结点。
遍历中序线索二叉树
算法 5.9(p132)
void InOrderTraverse_Thr(BiThrTree T)
//T 指向头结点,头结点的左链 lchild 指向树的根结点
{
p=T->lchild; // 从头结点进入到根结点;
while( p!=T) // 空树或遍历结束时, p==T
{
while(p->LTag==0) p=p->lchild; // 先找到中序遍历起点
cout<<p->data; // 访问左子树为空的结点
while(p->RTag==1 && p->rchild!=T) //p->rchild=T 即为最后一个结点
{
p=p->rchild; cout<<p->data;
}
p=p->rchild;
}
}
5.6 树和森林
5.6.1 树的存储结构
双亲表示法
孩子表示法
孩子兄弟表示法
5.6.2 森林与二叉树的转换
5.6.3 树和森林的遍历
5.6.1 树的存储结构
1 双亲表示法
实现
定义结构数组存放树的结点,每个结点含两个域:
数据域:存放结点本身信息
双亲域:指示本结点的双亲结点在数组中位置
特点 typedef struct PTNode{
找双亲容易,找孩子难 TElemType data;
int parent;
}PTNode;
typedef struct{
PTNode nodes[100];
int r,n; // 根的位置和结点数
}PTree ;
5.6.1 树的存储结构
1 双亲表示法 0 A -1
A
1 B 0
2 C 0
B C
3 D 1
4 E 1
D E F G
5 F 2
6 G 2
H I 7 H 3
8 I 3
缺点:求结点的孩子时需要遍历整个结构。
5.6.1 树的存储结构
孩子表示法
多重链表
每个结点有多个指针域,分别指向其子树的根
结点同构
» 结点的指针个数相等,为树的度 D
» 缺点:容易造成空间浪费
结点不同构
» 结点指针个数不等,为该结点的度 d
» 缺点:操作不方便
data degree child1 child2 … childd
5.6.1 树的存储结构
2 孩子表示法
孩子链表
每个结点的孩子结点用单链表存储,再用含 n 个元素
的结构数组指向每个孩子链表
typedef struct CTNode {
int child; // 孩子结点的序号
struct CTNode *next; // 孩子结点的指针域
} *ChildPtr; // 孩子链表的结点
typedef struct {
ElemType data; // 结点的数据元素
ChildPtr firstchild; // 孩子链表头指针
} CTBox;
typedef struct {
CTBox nodes[Maxnode];
int n, r; // 结点数和根结点的位置
} CTree; // 树结构
5.6.1 树的存储结构
2 孩子表示法
孩子链表
5.6.1 树的存储结构
2 孩子表示法
孩子链表
5.6.1 树的存储结构
3 孩子兄弟表示法
又称二叉树表示法,或二叉链表表示法
即以二叉链表作存储结构,链表中结点的两个链
域分别指向该结点的第一个孩子和下一个兄弟,
分别命名为 firstson 域和 nextsibling 域。
firstchild data nextsibling
B C E 存储 B
存储 ^ B
C
D C
D E
^ D ^ 解释
解释 A ^
^ E ^
A ^ ^ B
^ B C ^ E ^ C
^ D ^ ^ D ^ ^ E ^
树与二叉树的转换
将树转换成二叉树
加线:在兄弟之间加一连线
抹线:对每个结点,除了其左孩子外 , 去除其与其余孩子之间的关系
旋转:将同一双亲的孩子连线绕左孩子旋转 45 度角。
A A A
B C D B C D B C D
E F G H I E F G H I E F G H I
A
A B
E C
B C D
F D
E F G H I
G H
树转换成的二叉树其右子树一定为空
I
树与二叉树的转换
将二叉树转换成树
加线:若 p 结点是双亲结点的左孩子,则将 p 的右孩子,右孩子的
右孩子,……沿分支找到的所有右孩子,都与 p 的双亲用线连起来
抹线:抹掉原二叉树中双亲与右孩子之间的连线
调整:将结点按层次排列,形成树结构
A A A A
B B B B
E C E C E C E C
F D F D F D F D
G H G H G H G H
A I I I I
B C D
操作要点:把右孩子变为兄弟!
E F G H I
5.6.2 森林与二叉树的转换
由此,通过
存储结构的
一 致 性 作
为 " 媒
介 " ,可建
立森林和二
叉树之间一
一对应的关
系。
思考:如果没有告诉你,这是上图所示的森林的
存储结构,你是否会将它看成是一棵二叉树呢?
5.6.2 森林与二叉树的转换
森林和二叉树的对应关系
设森林
F = ( T1, T2, …, Tn )
T1 = ( root , t11, t12, …, t1m )
二叉树
B =( LBT, Node(root), RBT )
5.6.2 森林与二叉树的转换
由森林转换成二叉树的转换规则为 :
若 F = Φ ,则 B = Φ;
否则,
由 ROOT( T1 ) 对应得到
Node(root);
由 (t11, t12, …, t1m ) 对应得到
由LBT;
(T2, T3,…, Tn ) 对应得到 RBT 。
5.6.2 森林与二叉树的转换
root
T1
T2,…,Tn
T11,T12,…,T1m
LBT RBT
5.6.2 森林与二叉树的转换
森林转换为二叉树
即 F={T1, T2, …,Tm} B={root, LBT,
RBT}
T1 = ( root , t11, t12, …, t1m )
方法一:
若 F = Φ ,则 B = Φ; 否则,
① 将 F={T1, T2,⋯,Tn} 中的每棵树转换成二叉树。
② 按给出的森林中树的次序,从最后一棵二叉树
开始,每棵二叉树作为前一棵二叉树的根结点的
右子树,依次类推,则第一棵树的根结点就是转
换后生成的二叉树的根结点。
方法二:森林直接变兄弟,再转为二叉树
5.6.2 森林与二叉树的转换
方法二:森林直接变兄弟,再转为二叉树
A E G
A
B C D F H I
B E
J C F G
A
A E G
D H
B C D F H I I
兄弟相连 长兄为父 J
J
孩子靠左 头根为根
5.6.2 森林与二叉树的转换
二叉树转换成森林
抹线:将二叉树中根结点与其右孩子连线,及沿右分支搜索到的所有
右孩子间连线全部抹掉,使之变成孤立的二叉树
还原:将孤立的二叉树还原成树
A
A E G
B E
B H
G F
C F C I
H
D D J
I
J A E G
操作要点:把最右边的 B C D F H I
子树变为森林,其余右 J
子树变为兄弟
5.6.2 森林与二叉树的转换
练习 1
将下面由三棵树组成的森林转换成二叉树
5.6.2 森林与二叉树的转换
练习 2
将下面的二叉树转换成森林
5.6.3 树和森林的遍历
树的遍历
先根遍历
先访问树的根结点,然后依次先根遍历根的每棵子树
后根遍历
先依次后根遍历每棵子树,然后访问根结点
层次遍历
先访问第一层上的结点,然后依次遍历第二层,…,
第 n 层上的结点
5.6.3 树和森林的遍历
树的遍历 A
B C D
E F G H
I J K L M
N O
先根遍历:A B E F I G C D H J K L N O M
后根遍历:E I F G B C J K N O L M H D A
层次遍历:A B C D E F G H I J K L M N O
5.6.3 树和森林的遍历
A
B C D 先根遍历
ABEFCDG
E F G
A 树的先根遍历等价于
B 二叉树的先序遍历!
E C 先序遍历
ABEFCDG
F D
G
5.6.3 树和森林的遍历
A
B C D 后根遍历
EFBCGDA
E F G
A 树的后根遍历等价于
B 二叉树的中序遍历!
E C 中序遍历
EFBCGDA
F D
G
5.6.3 树和森林的遍历
森林的遍历
先序遍历
若森林不空,则可依下列次序进行遍历 :
访问森林中第一棵树的根结点;
先序遍历第一棵树中的子树森林;
先序遍历除去第一棵树之后剩余的树构成的森林。
中序遍历
若森林不空,则
中序遍历森林中第一棵树的子树森林 ;
访问森林中第一棵树的根结点 ;
中序遍历森林中 ( 除第一棵树之外 ) 其余树构成的森林。
5.6.3 树和森林的遍历
对下图所示森林进行先序和中序遍历
先序遍历: ABCDEFIGJH
中序遍历: BDCAIFJGHE
5.6.3 树和森林的遍历
练习:用先序和中序两种方法遍历该森林;
写出遍历后的结点序列。
树、森林的遍历和二叉
树遍历的对应关系 ?
树 森林 二叉树
路径长度 y z
路径上的分支数目
a b c d
带权路径长度
7 5 2 4
结点到根的路径长度与结点上权的乘积
第 1 步:初始化 2 4 5 3
第 2 步:选取与合 5
并
2 3
第 3 步:删除与加 4 5 5
入
2 3
5.7 赫夫曼树及其应用
W = {2 , 3 , 4 , 5} 哈夫曼树的构造过程
重复第 2 4 5 5
步
2 3
9
4 5
重复第 3 9 5
步
4 5 2 3
5.7 赫夫曼树及其应用
W = {2 , 3 , 4 , 5} 哈夫曼树的构造过程
重复第 2 9 5
步
4 5 2 3
重复第 3 14
步
9 5
4 5 2 3
5.7 哈夫曼树及其应用
基本思想:使权大的结点靠近根
7 5 2 4
a b c d
18
7 5 6 7 11
a b a 7
2 4 5 a
c d b 5
2 4 b
c d 2 4
c d
7
a
5 操作要点:对权值的合并、删除与替
b 换,总是合并当前值最小的两个
2 4
c d
练习
w={5, 29, 7, 8, 14, 23, 3, 11}
哈夫曼编码
在远程通讯中,要将待传字符转换成二进制
的字符串,怎样编码才能使它们组成的报文
在网络中传得最快?
A 00 A 0
B 01 ABACCDA B
C
00
1
C 10
D 11 D 01
000110010101100 000011010
出现次数较多的字符采用尽可能短的编码
哈夫曼编码
A 0
ABACCDA B
C
00
1
D 01
0000
AAAA ABA BB
重码 000011010
关键:要设计长度不等的编码,则必须使任一字符的
编码都不是另一个字符编码的前缀-前缀编码
哈夫曼编码
ABACCDA
A—0
采用二叉树设 B—110
计前缀编码 C—10
D—111
0 1
0110010101110
A 0 1
C 左分支用“ 0”
0 1
B D 右分支用“ 1”
哈夫曼编码的构造
基本思想
概率大的字符用短码,小的用长码
例:某系统在通讯时,只出现 C , A , S ,
T , B 五种字符,其出现频率依次为
2 , 4 , 2 , 3 , 3 ,试设计 Huffman 编
码。 14
T 00 0 1
B 01 6 8 1
0 1 0
A 10 3 3 4 4
0 1
C T B A 2 2
110 C S
S
哈夫曼编码的译码过程
分解接收字符串:遇“ 0” 向左,遇“ 1” 向
右;一旦到达叶子结点,则译出一个字符,
反复由根出发,直到译码完成。
1 0110010101110
0
A 0 1
C 0 1
B D
ABACCDA
特点:每一码都不是另一码的前缀,绝不会错译 !
哈夫曼树构造算法的实现
一棵有 n 个叶子结点的 Huffman 树有多少个
结点?
2n-1 typedef struct
采用顺序存储结构 { int weight;
结点类型定义 int
一维结构数组 parent,lchild,rchild;
}HTNode,*HuffmanTree
设置一个数组 huffTree[2n-1] 保存哈夫曼树中各点的信
息,数组元素的结点结构 ; :
初 态
赫夫曼树构造算法实现 - 举例
2 4 5 3 weight parent lchild rchild
i1
4 5 5 0 2 4 -1 -1 -1
1 4 -1 -1 -1
2 3
2 5 -1 -1 -1
i2
3 3 4 -1 -1 -1
k
4 5 -1 0 -1 3 -1
5 -1 -1 -1
6 -1 -1 -1
过程
赫夫曼树构造算法实现 - 举例
4 5 5 weight parent lchild rchild
0 2 4 -1 -1 -1
2 3 i1
1 4 5 -1 -1 -1
5 9
2 5 -1 -1 -1
4 5 3 3 4 -1 -1 -1
i2
4 5 5 -1 0 -1 3 -1
2 3 k 5
9 -1 1 -1 4 -1
6 -1 -1 -1
过程
赫夫曼树构造算法实现 - 举例
5 9 weight parent lchild rchild
0 2 4-1 -1 -1
4 5
1 4 5-1 -1 -1
i1
2 3 2 5 6-1 -1 -1
14 3 3 4-1 -1 -1
4 5 5-1 0 -1 3 -1
5 9 i2
5 9 6-1 1 -1 4 -1
4 5 k 6 14 -1 2 -1 5 -1
过程
2 3
哈夫曼编码的几点结论
哈夫曼编码是前缀编码,即任一字符的编码都不是
另一字符编码的前缀
哈夫曼编码树中没有度为 1 的结点。若叶子结点的
个数为 n ,则哈夫曼编码树的结点总数为 2n-1
发送过程
根据由哈夫曼树得到的编码表送出字符数据
接收过程
按左 0 、右 1 的规定,从根结点走到一个叶结点,完成一
个字符的译码。反复此过程,直到接收数据结束
练习
设有正文 AADBAACACCDACACAAD, 字符集为 A,B,C,D,
设计一套二进制编码,使得上述正文的编码最短。
计算它的带权路径长度。
字符 A , B , C , D 出现的次数为
9,1,5,3 。
树 相互转换 二叉树
抽 双 孩 孩 二 特 二 抽 二 顺
树 基 树 二 三 线
象 亲 子 子 叉 殊 叉 象 叉 序
的 本 数 的 表 表 兄 叉 叉 索
树 的 树 数 树 存
弟
定 术 据 遍 示 示 的 二 的 据 的 储 链 链 链
表
类 法 法 示 定 叉 性 类 遍 结
义 语 历 表 表 表
型 法 义 树 质 型 历 构
其他算法
小结
1 定义和性质
树 顺序结构
2 存储结构
二叉链表
链式结构
三叉链表
森林 二叉树 先序遍历
3 遍历 中序遍历
后序遍历 先序线索树
4 线索化:线索树 中序线索树
赫夫曼树 后序线索树
赫夫曼编码
小结
掌握二叉树的基本概念、性质和存储结构
熟练掌握二叉树的前、中、后序遍历方法
了解线索化二叉树的思想
熟练掌握:哈夫曼树的实现方法、构造哈夫
曼编码的方法
熟练掌握:森林与二叉树的转换,森林的遍
历方法
练习
一、选择题
1. 已知一算术表达式的中缀形式为 A+B*C-D/E ,
后缀形式为 ABC*+DE/- ,其前缀形式为 (D )
A . -A+B*C/DE B. -A+B*CD/E
C . -+*ABC/DE D. -+A*BC/DE
2. 设有一表示算术表达式的二叉树,它所表示的
算术表达式是( C ) /
A. A*B+C/(D*E)+(F-G) + +
B.(A*B+C)/(D*E)+(F-G) *
C
* -
D E F G
C.(A*B+C)/(D*E+ ( F-G ) )
A B
D. A*B+C/D*E+F-G
3. 在下述结论中,正确的是( D )
①只有一个结点的二叉树的度为 0;
② 二叉树的度为 2 ;
③二叉树的左右子树可任意交换 ;
④ 深度为 K 的完全二叉树的结点个数小于或等
于深度相同的满二叉树。
A .①②③ B .②③④ C .②④ D.
①④
4. 若一棵二叉树具有 10 个度为 2 的结点, B 5 个度
为 1 的结点,则度为 0 的结点个数是( )
A.9 B . 11 C . 15 B D .不
确定
5. 有关二叉树下列说法正确的是( )
A .二叉树的度为 2
B .一棵二叉树的度可以小于 2
6. 具有 10 个叶结点的二叉树中有(B )个
度为 2 的结点。
A.8 B.9 C . 10
D . ll
7. 一棵完全二叉树上有 1001 E 个结点,其中
叶子结点的个数是( )
A . 250 B . 500 C . 254 D .
505 E .以上答案都不对
由二叉树结点的公式: n=n0+n1+n2=n0+n1+(n0-
1)=2n0+n1-1 , 因为 n=1001, 所以 1002=2n0+n1,
在完全二叉树树中, n1 只能取 0 或 1, 在本题中
只能取 0 ,故 n=501 ,因此选 E 。
8. 二叉树的第 i 层上最多含有结点数为( C )
A . 2i B . 2i-1-1 C . 2i-1 D.
2i -1 C
9. 一个具有 1025 个结点的二叉树的高 h 为(
)
A . 11 B . 10 C . 11 至 1025 之间
D . 10 至 1024 之间 B
10. 一棵二叉树高度为 h, 所有结点的度或为 0 ,或
为 2 ,则这棵二叉树最少有 ( ) 结点 D
A . 2h B . 2h-1 C . 2h+1 D.
h+1
11. 对于有 n 个结点的二叉树 , 其高度为( )
A
A . nlog2n B . log2n
C . log2n+1 D .不确定
12. 一棵具有 n 个结点的完全二叉树的树高度
13. 深度为 h 的满 m 叉树的第 k 层有( A )个结点。
(1=<k=<h)
A . mk-1 B . mk-1 C . mh-1
D . mh-1
14.已知一棵二叉树的前序遍历结果为 ABCDEF,A
中序遍历结果为 CBAEDF, 则后序遍历的结果为 (
)
A . CBEFDA B . FEDCBA C.
CBEDFA D .不定 B
15.某二叉树中序序列为 A,B,C,D,E,F,G ,后序序
列为 B,D,C,A,F,G,E 则前序序列是:( )
A . E,G,F,A,C,D,B B . E,A,C,B,D,G,F
C . E,A,G,C,F,B,D D .上面的都不对 B
16.在二叉树结点的先序序列,中序序列和后序序
列中,所有叶子结点的先后顺序( )
A .都不相同 B .完全相同 C .先序和
中序相同,而与后序不同 D .中序和后序相同,
17. 一棵非空的二叉树的先序遍历序列与后序遍历
序列正好相反,则该二叉树一定满足( C )
A .所有的结点均无左孩子
B .所有的结点均无右孩子
C .只有一个叶子结点
D .是任意一棵二叉树
18. 引入二叉线索树的目的是( A )
A .加快查找结点的前驱或后继的速度
B .为了能在二叉树中方便的进行插入与删除
C .为了能方便的找到双亲
D .使二叉树的遍历结果唯一
19. n 个结点的线索二叉树上含有的线索数为(C
)
A . 2n B . n-1 C.n+1 D.
n
20. 树的后根遍历序列等同于该树对应的二叉树的
( B )
A. 先序序列 B. 中序序列 C. 后序序列 D. 顺序
序列
22. 在下列存储形式中,哪一个不是树的存储形式?
D
( )
A .双亲表示法 B .孩子链表表示法
C .孩子兄弟表示法 D .顺序存储表示法
23. 利用二叉链表存储树,则根结点的右指
针是( C )。
A .指向最左孩子 B .指向最
右孩子 C .空
D .非空
24. 设给定权值总数有 n 个,其哈夫曼树的
结点总数为( )
D 。
A .不确定 B . 2n C.
2n+1 D . 2n-1
25. 设 F 是一个森林, B 是由 F 变换得的二叉树。
若 F 中有 n 个非终端结点,则 B 中右指针域为空
的结点有( )个。
A . n-1 B.n C . n+1 D.
n+2
或为空树,或为任一结点至多只有右子树的二叉
树
9. 设 F 是由 T1,T2,T3 三棵树组成的森林 , 与 F 对
应的二叉树为 B, 已知 T1,T2,T3 的结点数分别
为 n1,n2 和 n3 则二叉树 B 的左子树中有(
n1-1
)个结点,右子树中有( n2+n3 )个结点。
10.有数据 WG={7,19,2,6,32,3,21,10}
则所建 Huffman 树的树高是( 6 ),
带权路径长度 WPL 是(261 )。
(10+7)*4+(2+3)*5+6*4+(19+21)*2+32*2=261
11.有一份电文中共使用 6 个字符 :a,b,c,d,e,f,
它们的出现频率依次为 2,3,4,7,8,9 ,试构造
一棵哈夫曼树,则其加权路径长度 WPL80 为(
),字符 c 的编码是( ) )
100/000/001/101…( 不唯一
(7+8)*2+4*3+(2+3)*4+9*2=80
2 、设一棵二叉树的先序遍历序列: A B D
F C E G H ,中序遍历序列: B F D A G E
H C ,画出这棵二叉树。
B C
D E
F G H
3 、已知一棵二叉树的中序: GLDHBEIACJFK
后序: LGHDIEBJKFCA ,画出这棵二叉树:
4 、一棵非空的二叉树其先序序列和后序序列
正好相反,画出这棵二叉树的形状。
分析:先序序列是“根左右” 后序序列是
“左右根”,可见对任意结点,若至多只有
左子女或至多只有右子女,均可使前序序列
与后序序列相反,图示如下:
5 、一棵二叉树的先序、中序、后序序列如下,
其中一部分未标出,请将其标出并构造出该
二叉树。
B _ _ C DF E _ G HJ I _ K
先序序列A :
中序序列 : CE BD _ _ HF A _ J K I G
后序序列C : _ E F KD B _ J IG H _ A
7 、设有正文 AADBAACACCDACACAAD, 字符集为
A,B,C,D, 设计一套二进制编码,使得上述正
文的编码最短。计算它的带权路径长度。
字符 A , B , C , D 出现的次数为
9,1,5,3 。
其 哈 夫 曼 编 码 如 下
A:1 , B:000 , C:01 , D:001
wpl=(1+3)*3+5*2+9*1=31