数据结构DOC讲义第5章.docx
《数据结构DOC讲义第5章.docx》由会员分享,可在线阅读,更多相关《数据结构DOC讲义第5章.docx(39页珍藏版)》请在冰豆网上搜索。
数据结构DOC讲义第5章
第五章树
树是一类重要的非线性数据结构,是以分支关系定义的层次结构
5.1树的定义
定义
定义:
树(tree)是n(n>0)个结点的有限集T,其中:
有且仅有一个特定的结点,称为树的根(root)
当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2,……Tm,其中每一个集合本身又是一棵树,称为根的子树(subtree)
特点:
树中至少有一个结点——根
树中各子树是互不相交的集合
根
子树
基本术语
结点(node)——表示树中的元素,包括数据项及若干指向其子树的分支
结点的度(degree)——结点拥有的子树数
叶子(leaf)——度为0的结点
孩子(child)——结点子树的根称为该结点的孩子
双亲(parents)——孩子结点的上层结点叫该结点的~
兄弟(sibling)——同一双亲的孩子
树的度——一棵树中最大的结点度数
结点的层次(level)——从根结点算起,根为第一层,它的孩子为第二层……
深度(depth)——树中结点的最大层次数
森林(forest)——m(m³0)棵互不相交的树的集合
结点A的度:
3
结点B的度:
2
结点M的度:
0
叶子:
K,L,F,G,M,I,J
结点A的孩子:
B,C,D
结点B的孩子:
E,F
结点I的双亲:
D
结点L的双亲:
E
结点B,C,D为兄弟
结点K,L为兄弟
树的度:
3
结点A的层次:
1
结点M的层次:
4
树的深度:
4
结点F,G为堂兄弟
结点A是结点F,G的祖先
5.2二叉树
定义
定义:
二叉树是n(n³0)个结点的有限集,它或为空树(n=0),或由一个根结点和两棵分别称为左子树和右子树的互不相交的二叉树构成
特点
每个结点至多有二棵子树(即不存在度大于2的结点)
二叉树的子树有左、右之分,且其次序不能任意颠倒
基本形态
二叉树性质
性质1:
性质3:
对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1
证明:
n1为二叉树T中度为1的结点数
因为:
二叉树中所有结点的度均小于或等于2
所以:
其结点总数n=n0+n1+n2
又二叉树中,除根结点外,其余结点都只有一个
分支进入
设B为分支总数,则n=B+1
又:
分支由度为1和度为2的结点射出,\B=n1+2n2
于是,n=B+1=n1+2n2+1=n0+n1+n2
\n0=n2+1
几种特殊形式的二叉树
满二叉树
定义:
特点:
每一层上的结点数都是最大结点数
完全二叉树
定义:
深度为k,有n个结点的二叉树当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时,称为~
特点
叶子结点只可能在层次最大的两层上出现
对任一结点,若其右分支下子孙的最大层次为l,则其左分支下子孙的最大层次必为l或l+1
性质
性质4:
性质5:
如果对一棵有n个结点的完全二叉树的结点按层序编号,则对任一结点i(1£i£n),有:
(1)如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是ëi/2û
(2)如果2i>n,则结点i无左孩子;如果2i£n,则其左孩子是2i
(3)如果2i+1>n,则结点i无右孩子;如果2i+1£n,则其右孩子是2i+1
5.3树的存储结构
树的存储结构
双亲表示法
实现:
定义结构数组存放树的结点,每个结点含两个域:
数据域:
存放结点本身信息
双亲域:
指示本结点的双亲结点在数组中位置
特点:
找双亲容易,找孩子难
typedefstructnode
{datatypedata;
intparent;
}JD;
JDt[M];
0
1
2
2
3
5
5
5
1
0号单元不用或
存结点个数
如何找孩子结点
孩子表示法
多重链表:
每个结点有多个指针域,分别指向其子树的根
结点同构:
结点的指针个数相等,为树的度D
结点不同构:
结点指针个数不等,为该结点的度d
孩子链表:
每个结点的孩子结点用单链表存储,再用含n个元素的结构数组指向每个孩子链表
孩子结点:
typedefstructnode
{intchild;//该结点在表头数组中下标
structnode*next;//指向下一孩子结点
}JD;
表头结点:
typedefstructtnode
{datatypedata;//数据域
structnode*fc;//指向第一个孩子结点
}TD;
TDt[M];//t[0]不用
^
^
^
^
^
如何找双亲结点
带双亲的孩子链表
孩子兄弟表示法(二叉树表示法)
实现:
用二叉链表作树的存储结构,链表中每个结点的两个指针域分别指向其第一个孩子结点和下一个兄弟结点
特点
操作容易
破坏了树的层次
typedefstructnode
{datatypedata;
structnode*fch,*nsib;
}JD;
^
^
^
^
^
^
^
^
^
^
二叉树的存储结构
顺序存储结构
实现:
按满二叉树的结点层次编号,依次存放二叉树中的数据元素
特点:
结点间关系蕴含在其存储位置中
浪费空间,适于存满二叉树和完全二叉树
链式存储结构
二叉链表
typedefstructnode
{datatypedata;
structnode*lchild,*rchild;
}JD;
在n个结点的二叉链表中,有n+1个空指针域
^
^
^
^
^
^
^
^
三叉链表
typedefstructnode
{datatypedata;
structnode*lchild,*rchild,*parent;
}JD;
^
^
^
^
^
^
^
^
^
树与二叉树转换
将树转换成二叉树
加线:
在兄弟之间加一连线
抹线:
对每个结点,除了其左孩子外,去除其与其余孩子之间的关系
旋转:
以树的根结点为轴心,将整树顺时针转45°
树转换成的二叉树其右子树一定为空
将二叉树转换成树
加线:
若p结点是双亲结点的左孩子,则将p的右孩子,右孩子的右孩子,……沿分支找到的所有右孩子,都与p的双亲用线连起来
抹线:
抹掉原二叉树中双亲与右孩子之间的连线
调整:
将结点按层次排列,形成树结构
森林转换成二叉树
将各棵树分别转换成二叉树
将每棵树的根结点用线相连
以第一棵树根结点为二叉树的根,再以根结点为轴心,顺时针旋转,构成二叉树型结构
二叉树转换成森林
抹线:
将二叉树中根结点与其右孩子连线,及沿右分支搜索到的所有右孩子间连线全部抹掉,使之变成孤立的二叉树
还原:
将孤立的二叉树还原成树
5.4树和二叉树的遍历
树的遍历
遍历——按一定规律走遍树的各个顶点,且使每一顶点仅被访问一次,即找一个完整而有规律的走法,以得到树中所有结点的一个线性排列
常用方法
先根(序)遍历:
先访问树的根结点,然后依次先根遍历根的每棵子树
后根(序)遍历:
先依次后根遍历每棵子树,然后访问根结点
按层次遍历:
先访问第一层上的结点,然后依次遍历第二层,……第n层的结点
先序遍历:
后序遍历:
层次遍历:
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
二叉树的遍历
方法
先序遍历:
先访问根结点,然后分别先序遍历左子树、右子树
中序遍历:
先中序遍历左子树,然后访问根结点,最后中序遍历右子树
后序遍历:
先后序遍历左、右子树,然后访问根结点
按层次遍历:
从上到下、从左到右访问各结点
DLR
先序遍历序列:
ABDC
先序遍历:
LDR
中序遍历序列:
BDAC
中序遍历:
LRD
后序遍历序列:
DBCA
后序遍历:
先序遍历:
中序遍历:
后序遍历:
层次遍历:
-
+
a
*
b
-
c
d
/
e
f
-
+
a
*
b
-
c
d
/
e
f
-
+
a
*
b
-
c
d
/
e
f
-
+
a
*
b
-
c
d
/
e
f
算法
递归算法
Ch5_1.c
voidpreorder(JD*bt)
{if(bt!
=NULL)
{printf("%d\t",bt->data);
preorder(bt->lchild);
preorder(bt->rchild);
}
}
返回
返回
返回
返回
A
C
B
D
返回
先序序列:
ABDC
非递归算法
Ch5_4.c
Ch5_40.C
后序遍历非递归算法
遍历算法应用
按先序遍历序列建立二叉树的二叉链表,已知先序序列为:
ABCFFDEFGFFFFFF
求二叉树深度算法
Ch5_5.c
ch5_6.c
Ch5_1.c
统计二叉树中叶子结点个数算法
5.5二叉树的应用
哈夫曼树(Huffman)——带权路径长度最短的树
定义
路径:
从树中一个结点到另一个结点之间的分支构成这两个结点间的~
路径长度:
路径上的分支数
树的路径长度:
从树根到每一个结点的路径长度之和
树的带权路径长度:
树中所有带权结点的路径长度之和
Huffman树——设有n个权值{w1,w2,……wn},构造一棵有n个叶子结点的二叉树,每个叶子的权值为wi,则wpl最小的二叉树叫~
例有4个结点,权值分别为7,5,2,4,构造有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
构造Huffman树的方法——Huffman算法
构造Huffman树步骤
根据给定的n个权值{w1,w2,……wn},构造n棵只有根结点的二叉树,令起权值为wj
在森林中选取两棵根结点权值最小的树作左右子树,构造一棵新的二叉树,置新二叉树根结点权值为其左右子树根结点权值之和
在森林中删除这两棵树,同时将新得到的二叉树加入森林中
重复上述两步,直到只含一棵树为止,这棵树即哈夫曼树
例w={5,29,7,8,14,23,3,11}
Huffman算法实现
Ch5_8.c
一棵有n个叶子结点的Huffman树有2n-1个结点
采用顺序存储结构——一维结构数组
结点类型定义
typedefstruct
{intdata;
intpa,lc,rc;
}JD;
Ch5_8.c
Huffman树应用
最佳判定树
Huffman编码:
数据通信用的二进制编码
思想:
根据字符出现频率编码,使电文总长最短
编码:
根据字符出现频率构造Huffman树,然后将树中结点引向其左孩子的分支标“0”,引向其右孩子的分支标“1”;每个字符的编码即为从根到每个叶子的路径上得到的0、1序列
例要传输的字符集D={C,A,S,T,;}
字符出现频率w={2,4,2,3,3}
T:
00
;:
01
A:
10
C:
110
S:
111
译码:
从Huffman树根开始,从待译码电文中逐位取码。
若编码是“0”,则向左走;若编码是“1”,则向右走,一旦到达叶子结点,则译出一个字符;再重新从根出发,直到电文结束
例电文是{CAS;CAT;SAT;AT}
其编码“110101*********00011111000011000”
电文为“1101000”
译文只能是“CAT”
线索二叉树
定义:
前驱与后继:
在二叉树的先序、中序或后序遍历序列中两个相邻的结点互称为~
线索:
指向前驱或后继结点的指针称为~
线索二叉树:
加上线索的二叉链表表示的二叉树叫~
线索化:
对二叉树按某种遍历次序使其变为线索二叉树的过程叫~
实现
在有n个结点的二叉链表中必定有n+1个空链域
在线索二叉树的结点中增加两个标志域
lt:
若lt=0,lc域指向左孩子;若lt=1,lc域指向其前驱
rt:
若rt=0,rc域指向右孩子;若rt=1,rc域指向其后继
结点定义:
typedefstructnode
{intdata;
intlt,rt;
structnode*lc,*rc;
}JD;
0
0
0
0
1
1
1
1
^
1
1
0
0
0
0
1
1
1
1
^
1
1
^
0
0
0
0
1
1
1
1
1
1
^
头结点:
lt=0,lc指向根结点
rt=1,rc指向遍历序列中最后一个结点
遍历序列中第一个结点的lc域和最后
一个结点的rc域都指向头结点
算法
按中序线索化二叉树
Ch5_20.c
JD*zxxsh(JD*bt)
{JD*p,*pr,*s[M],*t;
inti=0;
t=(JD*)malloc(sizeof(JD));
t->lt=0;t->rt=1;t->rc=t;
if(bt==NULL)t->lc=t;
else
{t->lc=bt;pr=t;p=bt;
do{while(p!
=NULL)
{s[i++]=p;p=p->lc;}
if(i>0)
{p=s[--i];
printf("%c",p->data);
if(p->lc==NULL)
{p->lt=1;p->lc=pr;}
if(pr->rc==NULL)
{pr->rt=1;pr->rc=p;}
pr=p;p=p->rc;
}
}while(i>0||p!
=NULL);
pr->rc=t;pr->rt=1;t->rc=pr;
}
return(t);
}
算法
按中序线索化二叉树
Ch5_20.c
JD*zxxsh(JD*bt)
{JD*p,*pr,*s[M],*t;
inti=0;
t=(JD*)malloc(sizeof(JD));
t->lt=0;t->rt=1;t->rc=t;
if(bt==NULL)t->lc=t;
else
{t->lc=bt;pr=t;p=bt;
do{while(p!
=NULL)
{s[i++]=p;p=p->lc;}
if(i>0)
{p=s[--i];
printf("%c",p->data);
if(p->lc==NULL)
{p->lt=1;p->lc=pr;}
if(pr->rc==NULL)
{pr->rt=1;pr->rc=p;}
pr=p;p=p->rc;
}
}while(i>0||p!
=NULL);
pr->rc=t;pr->rt=1;t->rc=pr;
}
return(t);
}
算法
按中序线索化二叉树
Ch5_20.c
JD*zxxsh(JD*bt)
{JD*p,*pr,*s[M],*t;
inti=0;
t=(JD*)malloc(sizeof(JD));
t->lt=0;t->rt=1;t->rc=t;
if(bt==NULL)t->lc=t;
else
{t->lc=bt;pr=t;p=bt;
do{while(p!
=NULL)
{s[i++]=p;p=p->lc;}
if(i>0)
{p=s[--i];
printf("%c",p->data);
if(p->lc==NULL)
{p->lt=1;p->lc=pr;}
if(pr->rc==NULL)
{pr->rt=1;pr->rc=p;}
pr=p;p=p->rc;
}
}while(i>0||p!
=NULL);
pr->rc=t;pr->rt=1;t->rc=pr;
}
return(t);
}
算法
按中序线索化二叉树
Ch5_20.c
i
输出:
B
JD*zxxsh(JD*bt)
{JD*p,*pr,*s[M],*t;
inti=0;
t=(JD*)malloc(sizeof(JD));
t->lt=0;t->rt=1;t->rc=t;
if(bt==NULL)t->lc=t;
else
{t->lc=bt;pr=t;p=bt;
do{while(p!
=NULL)
{s[i++]=p;p=p->lc;}
if(i>0)
{p=s[--i];
printf("%c",p->data);
if(p->lc==NULL)
{p->lt=1;p->lc=pr;}
if(pr->rc==NULL)
{pr->rt=1;pr->rc=p;}
pr=p;p=p->rc;
}
}while(i>0||p!
=NULL);
pr->rc=t;pr->rt=1;t->rc=pr;
}
return(t);
}
1
算法
按中序线索化二叉树
Ch5_20.c
输出:
B
i
P->C
JD*zxxsh(JD*bt)
{JD*p,*pr,*s[M],*t;
inti=0;
t=(JD*)malloc(sizeof(JD));
t->lt=0;t->rt=1;t->rc=t;
if(bt==NULL)t->lc=t;
else
{t->lc=bt;pr=t;p=bt;
do{while(p!
=NULL)
{s[i++]=p;p=p->lc;}
if(i>0)
{p=s[--i];
printf("%c",p->data);
if(p->lc==NULL)
{p->lt=1;p->lc=pr;}
if(pr->rc==NULL)
{pr->rt=1;pr->rc=p;}
pr=p;p=p->rc;
}
}while(i>0||p!
=NULL);
pr->rc=t;pr->rt=1;t->rc=pr;
}
return(t);
}
算法
按中序线索化二叉树
Ch5_20.c
i
P->C
输出:
B
JD*zxxsh(JD*bt)
{JD*p,*pr,*s[M],*t;
inti=0;
t=(JD*)malloc(sizeof(JD));
t->lt=0;t->rt=1;t->rc=t;
if(bt==NULL)t->lc=t;
else
{t->lc=bt;pr=t;p=bt;
do{while(p!
=NULL)
{s[i++]=p;p=p->lc;}
if(i>0)
{p=s[--i];
printf("%c",p->data);
if(p->lc==NULL)
{p->lt=1;p->lc=pr;}
if(pr->rc==NULL)
{pr->rt=1;pr->rc=p;}
pr=p;p=p->rc;
}
}while(i>0||p!
=NULL);
pr->rc=t;pr->rt=1;t->rc=pr;
}
return(t);
}
算法
按中序线索化二叉树
Ch5_20.c
i
输出:
BC
JD*zxxsh(JD*bt)
{JD*p,*pr,*s[M],*t;
inti=0;
t=(JD*)malloc(sizeof(JD));
t->lt=0;t->rt=1;t->rc=t;
if(bt==NULL)t->lc=t;
else
{t->lc=bt;pr=t;p=bt;
do{while(p!
=NULL)
{s[i++]=p;p=p->lc;}
if(i>0)
{p=s[--i];
printf("%c",p->data);
if(p->lc==NULL)
{p->lt=1;p->lc=pr;}
if(pr->rc==NULL)
{pr->rt=1;pr->rc=p;}
pr=p;p=p->rc;
}
}while(i>0||p!
=NULL);
pr->rc=t;pr->rt=1;t->rc=pr;
}
return(t);
}
1
算法
按中序线索化二叉树
Ch5_20.c
i
输出:
BC
JD*zxxsh(JD*bt)
{JD*p,*pr,*s[M],*t;
inti=0;
t=(JD*)malloc(sizeof(JD));
t->lt=0;t->rt=1;t->rc=t;
if(bt==NULL)t->lc=t;
else
{t->lc=bt;pr=t;p=bt;
do{while(p!
=NULL)
{s[i++]=p;p=p->lc;}
if(i>0)
{p=s[--i];
printf("%c",p->data);
if(p->lc==NULL)
{p->lt=1;p->lc=pr;}
if(pr->rc==NULL)
{pr->rt=1;pr->rc=p;}
pr=p;p=p->rc;
}
}while(i>0||p!
=NULL);
pr->rc=t;pr->rt=1;t->rc=pr;
}
return(t);
}
算法
按中序线索化二叉树
Ch5_20.c
i
输出:
BCA
JD*zxxsh(JD*bt)
{JD*p,*pr,*s[M],*t;
inti=0;
t=(JD*)malloc(sizeof(JD));
t->lt=0;t->rt=1;t->rc=t;
if(bt==NULL)t->lc=t;
else
{t->lc=bt;pr=t;p=bt;
do{while(p!
=NULL)
{s[i++]=p;p=p->lc;}
if(i>0)
{p=s[--i];
printf("%c",p->data);
if(p->lc==NULL)
{p->lt=1;p->lc=pr;}
if(pr->rc==NULL)
{pr->rt=1;pr->rc=p;}
pr=p;p=p->rc;
}
}while(i>0||p!
=NULL);
pr->rc=t;pr->rt=1;t->rc=pr;
}
return(t);
}
1
算法
按中序线索化二叉树
Ch5_20.c
i
输出:
BCA
P->D
JD