Upload
kezia
View
130
Download
0
Embed Size (px)
DESCRIPTION
第 6 章 树和二叉树. 特点: 非线性结构,一个直接前驱,但可能有多个 直接后继( 1 : n ). F 6.1 树的定义和基本术语 F 6.2 二叉树 F 6.3 遍历二叉树和线索二叉树 F 6.4 树和森林 F 6.6 赫夫曼树及其应用. 6.1 树的定义和基本术语. 1. 树的定义. 由一个或多个 ( n≥ 0 ) 结点组成的有限集合。在任何一棵非空树 T 中: ( 1 ) 有且仅有一个结点称为根 ( root ); ( 2 )当 n>1 时,其余的结点分为 m ( m≥ 0 )个 - PowerPoint PPT Presentation
Citation preview
1
6.1 树的定义和基本术语
6.2 二叉树
6.3 遍历二叉树和线索二叉树
6.4 树和森林
6.6 赫夫曼树及其应用
特点:非线性结构,一个直接前驱,但可能有多个 直接后继( 1 : n )
第 6 章 树和二叉树
2
1. 树的定义
注:树的定义具有递归性,即树中还有树。
由一个或多个( n≥ 0 )结点组成的有限集合。在任何一棵非空树 T 中:( 1 )有且仅有一个结点称为根( root );( 2 )当 n>1 时,其余的结点分为 m ( m≥ 0 )个 互不相交的有限集合 T1 、 T2…Tm 。每个 集合本身又是棵树,被称作这个根的子树。
6.1 树的定义和基本术语
3
A
只有根结点的树
A
B C D
E F G H I J
K L M
根
子树
6.1 树的定义和基本术语
4
树的抽象数据类型定义
D 是具有相同特性的数据元素的集合。ADT ADT TreeTree { {
}}TreeTree
数据对象 D:
数据操作 P:
数据关系 R: 若 D 是空集,则称为空树; // 允许 n=0若 D 中仅含一个数据元素,则 R 为空集;其他情况下的 R 存在二元关系:① Root 唯一 // 关于根的说明② Dj∩Dk= // 关于子树不相交的说明
③ …// 关于数据元素的说明
// 至少 15 个
6.1 树的定义和基本术语
5
•图形表示法•嵌套集合表示法•广义表表示法•凹入表示法(目录表示法)
树的表示方法
6.1 树的定义和基本术语
6
① 图形表示法:
…
大连工业大学
信息学院生物与食品学院 外语学院
数学 物理 电信 计算机
根
子树
A
B C D
E F G H
6.1 树的定义和基本术语
7
② 广义表表示法:
A
B C D
E F G H
根作为由子树森林组成的表的名字写在表的左边
A ( B , C , D)
A ( B , C ( E,F,G,H ), D)③ 凹入表示法: A
BC
D
EFGH
6.1 树的定义和基本术语
8
A
B C D
E F G H
④ 嵌套表示法:A
B DCE
G
F
H
6.1 树的定义和基本术语
9
2. 若干术语 A
B C D
JE F G H I
K L M
根 ---- 根结点(没有前驱) 森林 ---- 指 m 棵不相交的树的集合
有序树 ---- 结点各子树从左至右有序,不能互换(左为第一)
无序树 ---- 结点各子树可互换位置
双亲 ---- 上层的那个结点(直接前驱) 孩子 ---- 下层结点的子树的根(直接后继) 兄弟 ---- 同一双亲下的同一层结点(孩子之间互称兄弟)堂兄弟 ---- 双亲位于同一层的结点(但并非同一双亲) 祖先 ---- 从根到该结点所经分支的所有结点 子孙 ---- 该结点下层子树的任一结点
6.1 树的定义和基本术语
10
A
B C D
JE F G H I
K L M
结点 ---- 树中的数据元素 结点的度 ---- 结点拥有的子树的数目(有几个直接后继度就是几)结点的层次 ---- 从根到该结点的层数(根结点算第一层)
叶子 ---- 度为 0 的点(终端结点)
分支结点 ---- 度不为 0 的点(非终端结点)
树的度 ---- 所有结点度中的最大值( Max{ 各结点的度 } )树的深度 ---- 所有结点中最大的层数( Max{ 各结点的层次 } )(或高度)
6.1 树的定义和基本术语
11
二叉树的结构最简单,规律性最强; 可以证明,所有树都能转化为唯一对应的二叉树,不失一般性。
1. 二叉树的定义
2. 二叉树的性质
3. 二叉树的存储结构
6.2 二叉树
12
11 、二叉树的定义、二叉树的定义
① 每个结点最多只有两棵子树(不存在度大于 2的结点);② 左子树和右子树次序不能颠倒(有序树)。
基本特征:基本特征:
基本形态:基本形态:① ② ③ ④ ⑤
• 具有 3个结点的二叉树可能有几种不同形态?普通树呢?
6.2 二叉树 --- 定义
13
22 、二叉树的性质、二叉树的性质
性质 1 :在二叉树的第 i层上至多有 2i-1 个结点( i>0 )。问:第 i层上至少有 个结点?
性质 2 :深度为 k的二叉树至多有 2k-1 个结点( k>0 )。
性质 3 :对于任何一棵二叉树,若度 2的结点数有 n2个, 叶子结点数为 n0,则 n0=n2+1 。
6.2 二叉树 --- 性质
14
证明性质 3 :∵二叉树中全部结点数 n=n0+n1+n2( 叶子数 +度 1的结数 +度为 2的结
点数)又∵ 二叉树中全部结点数 n=B+1( 总分支数 + 根结点 )(除根结点外,每个结点必有一个直接前驱,即一个分支)
而 总分支数 B=n1+2n2( 度为 1必有 1个直接后继,度为 2必有 2个直接后继 )三式联立可得: n0+n1+n2=n1+2n2+1 ,即 n0=n2+1
6.2 二叉树 --- 性质
15
满二叉树:深度为 k 且有 2k-1 个结点的二叉树。 特点:每一层上的结点数都是最大结点数。 可以对满二叉树的结点进行连续编号。4
8 9
5
10 11
6
12 13
7
14 15
2 3
1
完全二叉树:深度为 k ,有 n 个结点的二叉树,当且仅当其每一个结点都与深度为 k 的满二叉树中编号从 1 至 n 的结点一一对应时,称之为完全二叉树。
特点 : ( 1 )叶子结点只可能在层次最大的两层上出现; ( 2 )对任一结点,若其右分支下的子孙的最大层次为 h , 则其左分支下的子孙的最大层次数必为 h 或 h+1 。
4
8 9
5
10
6 7
2 3
1
6.2 二叉树 --- 特例
16
1
2 3
11
4 5
8 9 12 13
6 7
10 14 15
1
2 3
11
4 5
8 9 12
6 7
10
1
2 3
4 5
6 7
1
2 3
4 5 6
6.2 二叉树 --- 特例
17
对于特殊性质的二叉树,还具备以下 2 个性质:性质 4 :具有 n个结点的完全二叉树的深度必为 loglog22nn ++ 1 。
性质 5 :如果对一棵有 n 个结点的完全二叉树 ( 其深度为 loglog22nn ++ 1) 的结点按层序编号,则对任一结点 i ( 1≤i≤n ),有: 1 )如果 i=1 ,则结点 i是二叉树的根,无双亲;如果 i>1 , 则其双亲 parent ( i)是结点 i/2 ; 2 )如果 2i>n ,则结点 i 无左孩子 ( 结点 i为叶子结点 ) ;否则其 左孩子 lchild ( i)是结点 2i ; 3 )如果 2i+1>n ,则结点 i无右孩子;否则其右孩子 rchild(i) 是结点 2i+1 。
4
8 9
5
10
6 7
2 3
1
6.2 二叉树 --- 性质
18
一、顺序存储结构33 、二叉树的存储结构、二叉树的存储结构
用一组地址连续的存储单元依次自上而下、自左至右存储二叉树上的结点元素。
D
H I
E F G
B C
A0123456789
ABCDEFGHI
D
F
E
B C
A
0123456789
ABCDE0000F
仅适合于完全二叉树对于非完全二叉树:将各层空缺处全部补上“虚结点”,其内容为0缺点:①浪费空间;②插入、删除不便
6.2 二叉树 --- 存储结构
19
二、链式存储结构
二叉链表中包含 2 个指针域。一般从根结点开始存储。
DATA
PARENT
LCHILD RCHILD
lchild data rchild
lchild data parent rchild
为了便于找到结点的双亲,可再增加一个双亲域指针,将二叉链表变成三叉链表。
链表的头指针指向二叉树的根结点。
6.2 二叉树 --- 存储结构
20
例:
C
E F
D
B
G
A A ^B
C D^ ^
E F^
G
^ ^
^ ^
A
B
^ ^
C D^ ^
E F^
G
^ ^
^ ^
二叉链表三叉链表
在含有 n 个结点的二叉链表中有 n+1 个空链域
空指针个数: 2*n0+1*n1+0*n2
=2n0+n1
=n0+n1+n0
=n0+n1+n2+1
=n+1
6.2 二叉树 --- 存储结构
21
一、遍历二叉树按某条搜索路径访问树中每个结点,使得每个结点均被访问一次,而且仅被访问一次。
二叉树由根、左子树、右子树构成,三者的组合可构成 6 种 遍历方案:根左右、根右左、左根右、左右根、右根左、右左根
若限定“先左后右”,则有 3 种实现方案: 根左右 左根右 左右根
先序遍历 中序遍历 后序遍历
6.3 遍历二叉树和线索二叉树
22
D
H I
F G
B C
A
1 、 先序遍历
2 、 中序遍历
3 、 后序遍历
①访问根结点②先序遍历左子树③先序遍历右子树
A BD CFG H I
①中序遍历左子树②访问根结点③中序遍历右子树
DB A FCHG I
①后序遍历左子树②后序遍历右子树③访问根结点
DB FHIGC A
例 1 :
6.3 遍历二叉树和线索二叉树
23
1 、 先序遍历
2 、 中序遍历
3 、 后序遍历
+ * * / AB C D E
A / B* C* D + E
A B / C * D
例 2 :
*
A B
/ C
* E
+
D
* E +
前缀表达式
中缀表达式
后缀表达式
6.3 遍历二叉树和线索二叉树
24
例:已知一棵二叉树的中序序列和后序序列分别是 BDCEAFHG 和 DECBHGFA ,请画出这棵二叉树。
讨论:若已知先序序列(或后序序列)和中序序列,能否恢复 出对应的二叉树?
分析:
①由后序遍历特征,根结点必在后序序列尾部(即 A)
②由中序遍历特征,根结点必在其中间,而且其左部必全部是左子树的子孙(即 BDCE),其右部必全部是右子树的子孙(即 FHG)
③继而,根据后序中的 DECB 子树可确定 B为 A的左孩子,根据 HGF子串可确定 F为 A的右孩子;以此类推
6.3 遍历二叉树和线索二叉树
25
已知中序遍历: B D C E A F H G
已知后序遍历: D E C B H G F A
( B D C E ) ( F H G )
A
( D C E )
B F
G
H
C
D E
A
ABC
6.3 遍历二叉树和线索二叉树
26
已知先序遍历序列如下: ABØDØØCEØFØØØ
A
B C
E
F
D
6.3 遍历二叉树和线索二叉树
27
二、线索二叉树普通二叉树只能找到结点的左右孩子的信息,而该结点的直接前驱和直接后继只能在遍历过程中获得。
若将遍历后对应的有关前驱和后继预存起来,则从第一个结点开始就能很快地遍历整个树了。
例如中序遍历结果: A/B*C*D+E ,实际上已将二叉树转为线性排列,显然具有唯一前驱和唯一后继。
如何预存这类信息呢?增加两个域 : 前驱指针、后继指针
利用 n+1 个空链域
6.3 遍历二叉树和线索二叉树
28
规定:1 )若结点有左子树,则 lchild 指向其左孩子; 否则, lchild 指向其直接前驱(即线索);2 )若结点有右子树,则 rchild 指向其右孩子; 否则, rchild 指向其直接后继(即线索);
lchild data rchild
约定: 当 Tag 域为 0时,表示孩子情况; 当 Tag 域为 1时,表示线索情况。
lchild LTag data RTag rchild
6.3 遍历二叉树和线索二叉树
29
有关线索二叉树的几个术语: 线索链表:用上述结点构成的二叉链表
线索:指向前驱和后继结点的指针
线索二叉树:同加上线索的二叉树(图形式样)
线索化:对二叉树以某种次序遍历使其变为线索二叉树的过程
线索化过程就是在遍历过程中修改空指针的过程:
将空的 lchild 改为结点的直接前驱;
将空的 rchild 改为结点的直接后继。
非空指针呢?仍然指向孩子结点(称为“正常情况”)
6.3 遍历二叉树和线索二叉树
30
例:画出下列二叉树所对应的中序线索二叉树
HH II
FF GG
BB CC
AA
DD EE
中序遍历结果: HDIBE A FCG
NIL
NIL
6.3 遍历二叉树和线索二叉树
31 HH II
FF GG
BB CC
AA
DD EE
NIL
NIL
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
存储结构
6.3 遍历二叉树和线索二叉树
中序遍历结果: HDIBE A FCG
32
1. 树的存储结构
2. 森林与二叉树的转换
3. 树和森林的遍历
6.4 树和森林
33
一、 树的存储结构树有三种常用存储方式:① 双亲表示法 ②孩子表示法 ③孩子兄弟表示法
1 、用双亲表示法来存储
方法:方法:用一组连续空间来存储树的结点,同时在每个结点中附设一个指示器,指示其双亲结点在链表中的位置。
6.4 树和森林 --- 树的存储
34
例 1 :双亲表示法
H
D E
F G
B C
A
双亲表示法
缺点:求结点的孩子时需要遍历整个结构
01234567
Data Parent
A -1 B 0 C 0 D 1 E 1 F 4 G 4 H 4
6.4 树和森林 --- 树的存储
35
2 、用孩子表示法来存储方法方法:将每个结点的孩子排列起来,形成一个带表头(装父亲结点)的线性表( n 个结点要设立 n 个链表),再将 n 个表头用数组存放起来,这样就形成一个混合结构。
H
D E
F G
B C
A
孩子表示法
B C ^ A D E ^ B
C ^ D ^
F G H ^ E F ^ G ^ H ^
01234567
6.4 树和森林 --- 树的存储
36
H
D E
F G
B C
A
带双亲的孩子表示法
Parent Data
01234567
ABCDEFGH
-10011444
B C ^
D E ^
F G H ^
^^
^^^
6.4 树和森林 --- 树的存储
37
3 、用孩子兄弟表示法来存储方法方法:用二叉链表来存储树,但链表的两个指针域含义不同。
firstchild data nextsibling
指向结点的第一个孩子 指向结点的第一个兄弟
H
D E
F G
B C
A
孩子兄弟表示法
A
B
^
D
C^ ^
^ E
F
^
^ G H^ ^ ^
6.4 树和森林 --- 树的存储
38
二、 森林与二叉树的转换
转换步骤转换步骤:① 结点的第一孩子做为其左孩子 ② 结点的右邻近兄弟做为其右孩子
H
D E
F G
B I
A
C
AA
BB
CC
IIDD
EE
FF
GG
HH
二叉树
1 、树与二叉树的转换
6.4 树和森林 --- 树与二叉树的转换
39
2 、二叉树与树的转换转换步骤转换步骤:把所有右孩子都变成其双亲的兄弟
AA
BB
CC
IIDD
EE
FF
GG
HH
AA
BB IICC
DD EE
FF HHGG
6.4 树和森林 --- 树与二叉树的转换
40
3 、森林与二叉树的转换转换步骤转换步骤:① 各树要先各自转为二叉树 ② 依次连接到前一个二叉树的右子树上
B D
A
C I
G
H
J
E
F
①AA
BB
IIFF
CC
JJDD
EE GG
HH II
JJ
GG
HH
FF
EE
AA
BB
CC
DD②
6.4 树和森林 --- 森林与二叉树的转换
41
三、树和森林的遍历1 、树的遍历 先序遍历
后序遍历
① 访问根结点② 依次先序遍历根结点的每棵子树
① 依次后序遍历根结点的每棵子树② 访问根结点D
B E
A
C
ABCD E
BDCEA
AA
BB
CC
EEDD先序:中序:后序:
ABCDEBDCEADECBA
结论:①树后序遍历相当于对应二叉树的中序遍历;② 树没有中序遍历,因为子树无左右之分。
6.4 树和森林 --- 树的遍历
42
2 、森林的遍历 先序遍历
中序遍历
①访问森林中第一棵树的根结点②先序遍历第一棵树中根结点的子树森林③先序遍历除去第一棵树之后剩余的树构成的森林
①中序遍历森林中第一棵树的根结点的子树森林②访问第一棵树的根结点③中序遍历除去第一棵树之后剩余的树构成的森林
B D
A
C I
G
H
J
E
F
ABCD E FGH I J
BCDAFEHJ I G
6.4 树和森林 --- 森林的遍历
43
一、 最优二叉树(赫夫曼树)
路 径:由一个结点到另一结点间的分支所构成。
路径长度:路径上的分支数目。
树的路径长度:从根到每一结点的路径长度之和。
带权路径长度:结点到根的路径长度与结点上权值的乘积。
树的带权路径长度:树中所有叶子结点的带权路径长度之和。
赫夫曼树:带权路径长度最小的树。
D E F G
B C
A
6.6 赫夫曼树及其应用
44
例:三棵二叉树中, 4个叶子结点 a、 b、 c、 d分别带权7、 5、 2、 4,它们的带权路径长度分别为 ?
① WPL=7*2+5*2+2*2+4*2=36 ② WPL=7*3+5*3+2*1+4*2=46 ③ WPL=7*1+5*2+2*3+4*3=35
a b c d d
a b
c
c d
b
a7
7
7
5
5
52
2
2
4 4
4①
② ③
WPL=∑WkLk
n
k=1Weighted Path Length
Huffman 树
6.6 赫夫曼树及其应用
45
二、构造赫夫曼树的基本步骤1 )根据给定的 n个权值 {ω1 ,ω2 ,……,ωn},构成 n棵二叉树的集合 F={T1 , T2 ,……, Tn},其中每棵二叉树 Ti 中只有一个带权为ωi 的根结点,其左右子树均空;
2)在 F中选取两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且新的二叉树的根结点的权值为其左、右子树上根结点的权值之和;
3)在 F中删除这两棵树,同时将新得到的二叉树加入 F中;
4)重复 2 )和 3),直到 F只含有一棵树为止。
6.6 赫夫曼树及其应用
46
例:画出以 7、 5、 2、 4 四个权值构造生成的赫夫曼树。
7 5 4 2 步骤 2 7 5
步骤3
7
4 2
65
11
4 2
6
4 骤步
4 2
65
117
18
步骤 1
一棵有 n 个叶子结点的赫夫曼树共有 2n-1 个结点。
6.6 赫夫曼树及其应用
47
三、赫夫曼编码设有电文‘ A B A C C D A’ 若 00、 01 、 10 、 11 ,则 00 01 00 10 10 11 00 ,总长 14 位;
若 0、 00 、 1、 01 ,则 0 00 0 1 1 01 0 ,总长 9位,但译码时产生二意性。
若要设计长短不等的编码,则必须是任一个字符的编码都不是另一个字符的编码的前缀,称为前缀编码。
6.6 赫夫曼树及其应用
48
c d
b
a左分支为 0
右分支为 1
Huffman 编码的结果是:a=0b=10c=110d=111
由于赫夫曼树的 WPL最小,说明编码所需要的长度最小。所以,这种编码已广泛应用于网络通信中。
设计电文总长度最短的二进制前缀编码即为以 n 种字符出现的频率作权值,设计一棵赫夫曼树的问题,由此得到的二进制前缀编码便称为赫夫曼编码。
6.6 赫夫曼树及其应用
49
例:已知某系统在通信联络中只可能出现八种字符,其概率分别为 0.07, 0.19, 0.02 , 0.06, 0.32 , 0.03 , 0.21 , 0.10 ,试设计赫夫曼编码。
解:先将概率放大 100倍,以方便构造赫夫曼树。 权值集合 w={7 , 19 , 2 , 6 , 32 , 3 , 21 , 10}
2 3
56
11
7 10
17
2832
60
19 21
40
100
11010.10010.21
11111
0.03100.32
11100.0611110
0.02000.19
11000.07编码频率
6.6 赫夫曼树及其应用
50
typedef struct{
int weight ; //权值分量(可放大取整) int parent , lchild , rchild ; // 双亲和孩子分量}HTNode , *HuffmanTree ; // 用动态数组存储 Huffman 树typedef char**HuffmanCode ; //动态数组存储 Huffman 编码表
1 、 Huffman 树和 Huffman 树编码的存储表示:
四、如何编程实现 Huffman 编码? 参见教材 P147
建议 1 : Huffman 树中结点的结构可设计成 4分量形式:
rchildlchildparentweight
建议 2 : Huffman 树的存储结构可采用顺序存储结构:将整个 Huffman 树的结点存储在一个数组 HT[1..n..m]中 ;各叶子结点的编码存储在另一“复合”数组 HC[1..n]中
指针型指针
6.6 赫夫曼树及其应用
51
2 、先构造 Huffman 树 HT
Void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int *w, int n){
if (n<=1)return;m=2*n-1; //n 个叶子的 HuffmanTree共有 2n-1 个结点;HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); //0 单元未用
*w 存放 n 个字符的权值
for(p=HT,i=1; i<=n; ++i,++p,++w)*p={*w,0,0,0}; //初始化前 n 个单元for( ; i<=m; ++i,++p) *p ={0,0,0,0}; // 对叶子之后的存储单元清零for(i=n+1;i<=m; ++i){ //建 Huffman 树 ( 从叶子开始 ) Select(HT, i-1, s1, s2); // 在 HT[1…i-1]选择 parent 为 0 且 weight 最小的两个结点,其序号分别为 S1 和s2 HT[s1].parent=i; HT[s2].parent=i; HT[i].lchild=s1; HT[i].rchild=s2; HT[i].weight=HT[s1].weight+ HT[s2].weight;}
6.6 赫夫曼树及其应用
52
3 、求出 n 个字符的 Huffman 编码 HC
HC=(HuffmanCode)malloc((n+1)*sizeof(char*)); // 分配 n 个字符编码的头指针 向量(一维数组)cd=(char*) malloc(n*sizeof(char)); // 分配求编码的工作空间 (n) cd[n-1]=‘\0’; // 编码结束符(从 cd[0]~cd[n-1] 为合法空间)for(i=1;i<=n;++i){ //逐个字符求 Huffman 编码 start=n-1; // 编码结束符位置 for(c=i,f=HT[i].parent; f!=0; c=f, f=HT[f].parent)// 从叶子到根逆向求编码
if(HT[f].lchild==c) cd[--start]=‘0’;else cd[--start]=‘1’;
HC[i]=(char*)malloc((n-start)*sizeof(char));// 为第 i 个字符编码分配空间 strcpy(HC[i],&cd[start]); // 从 cd复制编码串到 HC
}free(cd); //释放工作空间
}
6.6 赫夫曼树及其应用