树形结构.docx
《树形结构.docx》由会员分享,可在线阅读,更多相关《树形结构.docx(17页珍藏版)》请在冰豆网上搜索。
树形结构
树形结构——二叉树
相关知识:
一维数组|多维数组|栈|队列|串
一、树的基本术语
1.树的度——也即是宽度,以组成该树各结点中最大的度作为该树的度,如上图的树,其度为3;
2.树的深度——以组成该树各结点的最大层次,如上图,其深度为4;
3.森林——指若干棵互不相交的树的集合,如上图,去掉根结点A,其原来的二棵子树T1、T2、T3的集合{T1,T2,T3}就为森林;
4.有序树——指树中同层结点从左到右有次序排列,它们之间的次序不能互换,这样的树称为有序树,否则称为无序树。
二、树的表示
树的表示方法有许多,常用的方法是用括号:
先将根结点放入一对圆括号中,然后把它的子树由左至右的顺序放入括号中,而对子树也采用同样的方法处理;同层子树与它的根结点用圆括号括起来,同层子树之间用逗号隔开,最后用闭括号括起来。
如上图可写成如下形式:
(A(B(E(K,L),F),C(G),D(H(M),I,J)))
三、二叉树
1.二叉树的基本形态:
二叉树也是递归定义的,其结点有左右子树之分,逻辑上二叉树有五种基本形态:
(1)空二叉树——(a);
(2)只有一个根结点的二叉树——(b);
(3)右子树为空的二叉树——(c);
(4)左子树为空的二叉树——(d);
(5)完全二叉树——(e)
2.两个重要的概念:
(1)完全二叉树——只有最下面的两层结点度小于2,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树;
(2)满二叉树——除了叶结点外,每一个结点都有左右子女的二叉树。
如下图:
满二叉树
完全二叉树
3.二叉树的性质
(1)在二叉树中,第i层的结点总数不超过2^(i-1);
(2)对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点(有左右子节点)总数为N2,则N0=N2+1;
如上图(左):
N0=6,N2=5右图:
N0=8,N2=7(3)有N个结点的完全二叉树各结点如果用顺序方式表示,则结点之间有如下关系:
·如果I<>1,则其父结点的编号为[I/2];
·如果2*I<=N,则其左儿子(即左子树的根结点)的编号为2*I;若2*I>N,则无左儿子;
·如果2*I+1<=N,则其右儿子的结点编号为2*I+1;若2*I+1>N,则无右儿子。
(4)具有n个节点的完全二叉树的深度为[log2N]+1
4.二叉树的存储结构:
(1)顺序存储方式
图5
问题:
对于用顺序结构来表示的二叉树
A.如何根据节点顺序编号确定所层数和从左数起的编号;
反之如何根据层数和从左数起的编号确定节点顺序编号
B.如何根据节点顺序编号确定其父节点编号。
C.如何根据节点顺序编号确定其左右子节点编号
(2)链表存储方式,
A.采用数组类型
constmax=……{二叉树的存储容量}
TYPE
Datetype=……{节点信息类型}
Nodetype=recod
Data:
datatype;{信息域}
Lchild,rchild:
integer;{左右指针}
End;
VARlist:
array[1..max]ofnodetype;
应用形式:
list[I].data=……
list[list[I].lchild]:
=....{左节点}
list[list[I].rchild]:
=....{右节点}
如:
图5
数组下标:
1 2 3 4 5 6 7 8
数组D:
A B C D E F G H
左指针数组L:
2 4 6 0 7 0 0 0
右指针数组R:
3 5 0 0 0 8 0 0
B.采用动态数据类型
type
datatype=....
link=^nodetype
nodetype=recod
data:
datatype;{信息域}
lchild,rchild:
link;{左右指针}
end;
VARt:
link;
应用形式:
t^.data:
=....;t^.lchild:
=..;{左子节点}t^.rchild:
=..;{右子节点}
5.二叉树的遍历运算(递归定义)
A
BC
DEF
GHI
图6
(1)先序遍历ABDEGCFHI
递归定义
访问根;
按先序遍历左子树;
按先序遍历右子树。
算法如下
动态类型数组类型
typeconstmax=……
datatype=....Datetype=……
link=^nodetypeNodetype=recod
nodetype=recodData:
datatype;
data:
datatype;L,r:
integer;
l,r:
link;end;
end;VARlist:
array[1..max]ofnodetype;
VARt:
link;
proceduremake(t:
link);proceduremake(t:
intger);
beginbegin
ift<>nilthenift<>0then
beginbegin
访问t^.data访问list[t].data
make(t^.l);make(list[t].l);
make(t^.r);make(list[t].r);
end;end;
end;end;
(2)中序遍历DBGEACHFI
递归定义
按中序遍历左子树;
访问根;
按中序遍历右子树
proceduremake(t:
link);proceduremake(t:
intger);
beginbegin
ift<>nilthenift<>0then
beginbegin
make(t^.l);make(list[t].l)
访问t^.data访问list[t].data
make(t^.r);make(list[t].r);
end;end;
end;end;
(3)后序遍历DGEBHIFCA
递归定义
按后序遍历左子树;
按后序遍历右子树;
访问根
proceduremake(t:
link);proceduremake(t:
intger);
beginbegin
ift<>nilthenift<>0then
beginbegin
make(t^.l);make(list[t].l)
make(t^.r);make(list[t].r);
访问t^.data访问list[t].data
end;end;
end;end;
练习题
1.用顺序存储方式建立一棵深度为N(N<6)的满二叉树,并对其进行先序遍历。
(tree1.pas)
2.用链表存储方式建立一棵如图5所示的二叉树,并对其进行先序遍历。
(tree2.pas)
3.如图所示的二叉树,按先序边历读入字符串ABC##DE#G##F###(其中#表示对应的指针为空格),试用链表存储方式建立二叉数,然后用中序边历输出。
(tree4.pas)
参考程序
【tree1.pas】
programtree1;
var
b:
array[1..31]ofchar;
e:
array[1..63]ofbyte;
n,h,i,k:
integer;
proceduretree(t:
integer);
begin
ife[t]=0thenexit
elsebegin
write(b[t]);e[t]:
=0;
t:
=2*t;tree(t);
t:
=t+1;tree(t);
end;
end;
begin
repeat
write('n=');readln(n);
until(n>0)and(n<6);
fillchar(e,sizeof(e),0);
k:
=trunc(exp(n*ln
(2)))-1;
fori:
=1tokdo
e[i]:
=1;
fori:
=1to26do
b[i]:
=chr(64+i);
fori:
=1to5do
b[26+i]:
=chr(48+i);
h:
=1;
tree(h);
writeln;
end.
【tree2.pas】
programtree2;
const{人工建立二叉树的链表关系}
a:
array[1..8]ofchar=('A','B','C','D','E','F','G','H');
r:
array[1..8]ofinteger=(3,5,0,0,0,8,0,0);
l:
array[1..8]ofinteger=(2,4,6,0,7,0,0,0);
var
e:
array[1..7]ofinteger;
proceduresearch(m:
integer);
begin
ife[m]=0thenbegin
write(a[m]);e[m]:
=1
end;
if(l[m]=0)and(r[m]=0)thenexit
elsebegin
ifl[m]<>0thensearch(l[m]);
ifr[m]<>0thensearch(r[m]);
end;
end;
begin
fillchar(e,sizeof(e),0);
search
(1);
writeln;
end.
【tree4.pas】仅给出建树过程
procedurecrt(varbt:
指针记录类型){按先序边历建树过程}
VAR....
Begin
Read(ch);
Ifch=’#’thenbt:
=nil
Else
New(bt);bt^.data:
=ch;{建根节点}
Crt(bt^.l);{建左子树}
Crt(bt^.r);{建右子树}
End;
6.二叉排序树
排序树T是一棵按中序已分类的二叉树,即对于树的每个节点(包括顶点),值比它小的那些节点都在它的左边,值比它大的的右边。
按中序遍历,即可得到各个节点的信息域(data)按从小到大顺序的序列。
如图是一棵二叉排序树:
7
3
11
15913
(1)下列过程是以tree为根的一棵排序二叉中插入一个信息域值为newdata的节点。
Procedureinsert(vartree:
link;newdata:
datatype);
Begin
Iftree=nilthen
Begin
New(tree);
Tree^.l:
=nil;tree^.r:
=nil;tree^.data:
=newdata;{插入新节点}
End
Else
IfnewdataInsert(tree^.l,newdata);{新节点插在左子树中,以tree^.l为子树继续寻找插入位置}
Else
Insert(tree^.r,newdata);{新节点插在右子树中,以tree^.r为子树继续寻找插入位置}
End;
主程序框架如下
vartemp:
datatype;{节点类型}
root:
link;{根}
begin
root:
=nil;
repeet
read(temp);
iftemp<>’#’then{#是结束标志}
insert(root,temp);
untiltemp=’#’
end.
(2)如何在一棵二叉排序树中找出具有关键字V的节点,我们可采用分割查找,程序如下:
procedurefind(t:
link;v:
datatype);
begin
ift=nilthen
begin
writeln(‘Notfind!
’);
exit;
end;
else
begin
ift^.data>vthenfind(t^.l);
elseift^.dataelse
begin
writeln(‘find!
’);
exit;
end;
end;
end;
(3)练习题
.给出一组数据:
R={10.18,3,8,12,2,7,3},试编程序,先构造一棵二叉排序树,然后以中序遍历访问所得到的二叉树(即按数据域的值从小到大输出),并输出遍历结果。
(tree3.pas)
【tree3.pas】构造的二叉排序树如下
programtree3;
const
a:
array[1..8]ofinteger=(10,18,3,8,12,2,7,3);
type
point=^nod;
nod=record
w:
integer;
right,left:
point;
end;
var
first:
point;
j,k:
integer;
procedurehyt(d:
integer;varp:
point);
begin
ifp=nilthenbegin
new(p);
ifk=1thenbegin
first:
=p;
k:
=2
end;
withp^do
begin
w:
=d;
right:
=nil;
left:
=nil;
end;
end
else
withp^do
ifd>=wthenhyt(d,right)elsehyt(d,left);
end;
procedurehyt1(p:
point);
begin
withp^do
begin
ifleft<>nilthenhyt1(left);
write(w:
4);
ifright<>nilthenhyt1(right);
end
end;
begin
first:
=nil;
k:
=1;
forj:
=1to8dohyt(a[j],first);
hyt1(first);
writeln
end.
6.普通树转换成二叉树:
凡是兄弟就用线连起来,然后去掉父亲到儿子的连线,只留下父母到其第一个子女的连线。
AA
BCDB
EFGHIEC
普通树FD
GH
转换成二叉树I
四、例:
1.给出八枚金币a,b,c,d,e,f,g,h,编程以称最少的次数,判定它们蹭是否有假币,如果有,请找出这枚假币,并判定这枚假币是重了还是轻了。