数据结构(C语言版):第6章 树_第1页
数据结构(C语言版):第6章 树_第2页
数据结构(C语言版):第6章 树_第3页
数据结构(C语言版):第6章 树_第4页
数据结构(C语言版):第6章 树_第5页
已阅读5页,还剩111页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

树和二叉树1、树和森林的概念(树的定义、树的术语、性质及运算);2、二叉树的定义、性质及运算;3、二叉树的存储结构(顺序、链式表示);4、遍历二叉树5、树的存储结构;树、森林与二叉树的转换;遍历树;遍历森林6、哈夫曼树、哈夫曼编码。教学内容树型结构(非线性结构)结点之间有分支具有层次关系例自然界:树人类社会家谱行政组织机构计算机领域编译:用树表示源程序的语法结构数据库系统:用树组织信息算法分析:用树描述执行过程国务院山东省北京市西藏自治区…济南市青岛市威海市…历下区市中区…商河县6.1树的定义和基本术语定义:

(Tree)是n(n≥0)个结点的有限集。若n=0,称为空树;若n>0,则它满足如下两个条件:

(1)有且仅有一个特定的称为根

(Root)的结点;

(2)其余结点可分为m(m≥0)个互不相交的有限集T1,T2,

T3,…,Tm,其中每一个集合本身又是一棵树,并称为根的子树

(SubTree)。显然,树的定义是一个递归的定义。树的逻辑结构:树中任一结点都可以有零个或多个直接后继结点但至多只能有一个直接前趋结点。T3T2T1基本术语:结点的度:结点拥有的子树数。度

=0叶子

终端结点

度≠0分支结点

非终端结点

根结点以外的分支结点称为内部结点

树的度:树内各结点的度的最大值。双亲孩子兄弟结点的祖先:从根到该结点所经分支上的所有结点。结点的子孙:以某结点为根的子树中的任一结点。第1层第2层第3层第4层堂兄弟双亲在同一层的结点树的深度:树中结点的最大层次。有序树:树中结点的各子树从左至右有次序(最左边的为第一个孩子)。无序树:树中结点的各子树无次序。结点:数据元素+指向子树的分支根结点:非空树中无前驱结点的结点森林:是m(m≥0)棵互不相交的树的集合。一棵树可以看成是一个特殊的森林。把根结点删除树就变成了森林。给森林中的各子树加上一个双亲结点,森林就变成了树。树森林一定是不一定是EFGHIABCDJKLM树的逻辑结构描述一棵树的逻辑结构可以用二元组描述为:

Tree=(root,F)数据元素根结点包含m(m≥0)棵树的森林F=(T1,T2,…,Tm)Ti=(ri

,Fi

)Ti

称做根root

的第i

棵子树。当m≠0时,在树根和其子树森林之间存在下列关系:

RF={<root,ri

>|i=1,2,…,m,m>0}RF={<A,B

>,<A,C

>,<A,D

>}Tree=(A,F)F=(T1,T2,T3)T1=(B,F1)T2=(C,F2)T3=(D,F3)……r1F1={<B,E

>,<B,F

>}r2F2={<C,G

>}r3F3={<D,H

>,<D,I

>,<D,J

>}……EFGHIABCDJKLM树的抽象数据类型定义:ADTTree{

数据对象

D:D是具有相同特性的数据元素的集合。

数据关系

R:(略)

基本操作P:

{结构初始化}

InitTree(&T);

操作结果:构造空树T。

CreateTree(&T,definition);

初始条件:

definition给出树T的定义。

操作结果:按definition构造树T。

{销毁结构}

DestroyTree(&T);

初始条件:树T存在。操作结果:销毁树T。

{引用型操作}

TreeEmpty(T)

初始条件:树T存在。

操作结果:若T为空树,则返回TURE,否则FALSE。

TreeDepth(T)

初始条件:树T存在。

操作结果:返回T的深度。Root(T)

初始条件:树T存在。操作结果:返回T的根。Value(T,cur_e);

初始条件:树T存在,cur_e是T中某个结点。操作结果:返回cur_e的值。

Assign(T,cur_e,value)

初始条件:树T存在,cur_e是T中某个结点。

操作结果:结点cur_e赋值为value。

Parent(T,cur_e)

初始条件:树T存在,cur_e是T中某个结点。

操作结果:若cur_e是T的非根结点,则返回它的双亲,否则函数值为“空”。

LeftChild(T,cur_e)

初始条件:树T存在,cur_e是T中某个结点。

操作结果:若cur_e是T的非叶子结点,则返回它的最左孩子,否则返回“空”。RightSibling(T,cur_e)

初始条件:树T存在,cur_e是T中某个结点。

操作结果:若cur_e有右兄弟,则返回它的右兄弟,否则函数值为“空”。TraverseTree(T,Visit());

初始条件:树T存在,Visit是对结点操作的函数。

操作结果:按某种次序对T的每个结点调用函数

Visit()一次且至多一次。一旦Visit()

失败,则操作失败。{加工型操作}ClearTree(&T);

初始条件:树T存在。

操作结果:将树T清为空树。InsertChild(&T,&p,i,c);

初始条件:树T存在,p指向T中某个结点,1≤i≤p

所指结点的度+1,非空树c与T不相交。

操作结果:插入c为T中p指结点的第i棵子树。

DeleteChild(&T,&p,i);

初始条件:树T存在,p指向T中某个结点,

1≤i≤p所指结点的度。

操作结果:删除T中p所指结点的第i棵子树。}ADTTree树的表示形式

1.树形表示法

A空树AB仅含有根结点的树2.嵌套集合(文氏)表示法ABEFKLDHIMJCGEFGHIABCDJKLM3.凹入表示法ABEKLFCGDHMIJ▲4.广义表表示法EFGHIABCDJKLM(A(B(E(K,L),F),C(G),D(H(M),I,J)))6.2二叉树二叉树在树结构的应用中起着非常重要的作用,因为对二叉树的许多操作算法简单,而任何树都可以与二叉树相互转换,这样就解决了树的存储结构及其运算中存在的复杂性。6.2.1二叉树的定义二叉树是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的分别称作这个根的左子树和右子树的二叉树组成。定义特点1、每个结点最多有俩孩子(二叉树中不存在度大于2的结点)。2、子树有左右之分,其次序不能颠倒。3、二叉树可以是空集合,根可以有空的左子树或空的右子树。二叉树结点的子树要区分左子树和右子树,即使只有一棵子树也要进行区分,说明它是左子树,还是右子树。树当结点只有一个孩子时,就无须区分它是左还是右的次序。(也就是二叉树每个结点位置或者说次序都是固定的,可以是空,但是不可以说它没有位置,而树的结点位置是相对于别的结点来说的,没有别的结点时,它就无所谓左右了),因此二者是不同的。这是二叉树与树的最主要的差别。二叉树不是树的特殊情况,它们是两个概念。注AB具有两个结点的树只有一种状态ABAB具有两个结点的二叉树有两种状态

二叉树的5种基本形态

(a)空二叉树(b)根和空的左右子树(c)根和左子树(d)根和右子树

(e)根和左右子树注:虽然二叉树与树概念不同,但有关树的基本术语对二叉树都适用。二叉树的抽象数据类型定义:

ADTBinaryTree{

数据对象

D:D是具有相同特性的数据元素的集合。

数据关系

R:(略)

基本操作

P:

{结构初始化}

InitBiTree(&T);

操作结果:构造空二叉树T。

CreateBiTree(&T,definition);

初始条件:definition给出二叉树T的定义。

操作结果:按definition构造二叉树T。

{销毁结构}

DestroyBiTree(&T);

初始条件:二叉树T存在。

操作结果:销毁二叉树

T。

{引用型操作}

BiTreeEmpty(T);

初始条件:二叉树T存在。

操作结果:若T为空二叉树,则返回TRUE,否则返回

FALSE。

BiTreeDepth(T);初始条件:二叉树T存在。操作结果:返回T的深度。

Root(T);

初始条件:二叉树T存在。

操作结果:返回T的根。

Value(T,e);

初始条件:二叉树T存在,e是T中某个结点。

操作结果:返回e的值。

Parent(T,e);

初始条件:二叉树T存在,e是T中某个结点。

操作结果:若e是T的非根结点,则返回它的双亲,否则返回“空”。

LeftChild(T,e);

初始条件:二叉树T存在,e是T中某个结点。

操作结果:返回e的左孩子。若e无左孩子则返回“空”。

RightChild(T,e);

初始条件:二叉树T存在,e是T中某个结点。

操作结果:返回e的右孩子。若e无右孩子则返回“空”。

LeftSibling(T,e);

初始条件:二叉树T存在,e是T中某个结点。

操作结果:返回e的左兄弟。若e是其双亲的左孩子或无左兄弟,则返回“空”。

RightSibling(T,e);

初始条件:二叉树T存在,e是T的结点。

操作结果:返回e的右兄弟。若e是其双亲的右孩子或无右兄弟,则返回“空”。

PreOrderTraverse(T,visit());

初始条件:二叉树T存在,visit是对结点操作的应用函数。

操作结果:先序遍历

T,对每个结点调用函数visit一次且仅一次。一旦visit()失败,则操作失败。

InOrderTraverse(T,vsit());

初始条件:二叉树T存在,visit是对结点操作的应用函数。

操作结果:中序遍历

T,对每个结点调用函数Visit一次且仅一次。一旦visit()失败,则操作失败。

PostOrderTraverse(T,visit());

初始条件:二叉树T存在,visit是对结点操作的应用函数。

操作结果:后序遍历

T,对每个结点调用函数visit一次且仅一次。一旦visit()失败,则操作失败。

LevelOrderTraverse(T,visit());

初始条件:二叉树T存在,visit是对结点操作的应用函数。

操作结果:按层次遍历T,对每个结点调用函数visit一次且仅一次。一旦visit()失败,则操作失败。

{加工型操作}

ClearBiTree(&T);

初始条件:二叉树T存在。

操作结果:将二叉树T清为空树。

Assign(&T,&e,value);

初始条件:二叉树T存在,e是T中某个结点。

操作结果:结点e赋值为value。

InsertChild(&T,p,LR,c);

初始条件:二叉树T存在,p指向T中某个结点,LR

为0或1,非空二叉树c与T不相交且右子树为空。

操作结果:根据LR为0或1,插入c为T中p所指结点的左或右子树。p所指结点原有左或右子树成为c的右子树。

DeleteChild(&T,p,LR);

初始条件:二叉树T存在,p指向T中某个结点,LR

为0或1。

操作结果:根据LR为0或1,删除T中p所指结点的左或右子树。}ADTBinaryTree

6.2.2二叉树的性质性质1:在二叉树的第i

层上至多有2

i-1个结点(i≥1)。证:采用归纳法证明此性质。归纳基:当i=1时,只有一个根结点,2

i-1=20=1,命题成立。归纳假设:设对所有的j(1≤j<i),命题成立,即第j

层上至多有2j-1

个结点。那么可以证明j=i

时命题也成立。归纳证明:由归纳假设可知,第i–1层上至多有2

i-2个结点。由于二叉树每个结点的度最大为2,故在第i

层上最大结点数为第i–1层上最大结点数的2倍,即:

2×2

i–2=2

i–1。证毕。性质2:深度为k

的二叉树至多有2k-1个结点(k≥1)。证:由性质1可知,深度为k

的二叉树的最大结点数为:证毕。性质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=n1+2n2+1

即:n0=n2+1证毕。满二叉树(Fullbinarytree)特点:每一层上的结点数都达到最大。叶子全部在最底层。编号规则:从根结点开始,自上而下,自左而右。一棵深度为k

且有2k-1个结点的二叉树称为满二叉树。2453671完全二叉树(Completebinarytree)深度为k

的具有n

个结点的二叉树,当且仅当其每一个结点都与深度为k

的满二叉树中编号为1~n

的结点一一对应时,称之为完全二叉树。特点:叶子只可能分布在层次最大的两层上。

对任一结点,如果其右子树的最大层次为l,则其左子树的最大层次必为l

或l+1。满二叉树完全二叉树是定一不一定是

245361完全二叉树245361非完全二叉树完全二叉树的性质性质4:具有n

个结点的完全二叉树的深度为log2n+1。证:假设此二叉树的深度为k,则根据性质2及完全二叉树的定义得到:2k-1–1<n≤2k–1

或2k-1≤n<2k

取对数得:k–1≤log2n<k

因为k

是整数,所以有:

k=log2n+1▲性质5:如果对一棵有n

个结点的完全二叉树(深度为log2n+1)

的结点按层序编号(从第1层到第log2n+1层,每层从左到右),则对任一结点i(1≤i≤n),有:

(1)如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结点i/2。

(2)如果2i>n,则结点i为叶子结点,无左孩子;否则,其左孩子是结点2i。

(3)如果2i+1>n,则结点i

无右孩子;否则,其右孩子是结点2i+1。证:(1)可以从(2)和(3)推出,所以先证明(2)和(3)。

对于i=1,由完全二叉树的定义,其左孩子是结点2=2i,若2=2i>n=1,即不存在结点2,此时,结点i

无左孩子。(2)得证。i1结点i

的右孩子也只能是结点3=2i+1,若

3=2i+1>n,即不存在结点

3,此时结点i

无右孩子。

(3)得证。2i22i+132i2

(1)设第j(1≤j≤log2n)层的第一个结点的编号为i(由二叉树的定义和性质2知i=2

j–1),则其左孩子必为第j+1层的第一个结点,其编号为2

j=2×2

j–1=2i。

1

…第j

…23

对于

i>1,可分为两种情况:第j–1层第j+1层

i2

j-12i=2j

…2i+12

j-1-1=2j-1

如果2i>n,则无左孩子。(2)得证。其右孩子必定为第j+1层的第二个结点,编号为2i+1。若2i+1>n,则无右孩子。(3)得证。

(2)设第j(1≤j≤log2n)层的某个结点的编号为i(2

j–1≤i<2

j–1),且2i+1<n,其左孩子为2i,右孩子为2i+1。

2i…1…第j

…2i第j–1层第j+1层2

j-1-1i2

j-1…2i+12

j-1……i+12i2i+22i+3若它有左孩子,则其编号必定为:2i+2=2(i+1),(2)得证。若它有右孩子,则其编号必定为:2i+3=2(i+1)+1。(3)得证。

则编号为i+1的结点是编号为i

的结点的右兄弟或堂兄弟。下面证明(1)。当i=1时:此结点就是根,因此无双亲。当i>1时:如果i

为左孩子,且i

的双亲为p,则有i=2p,

p=i/2=i/2,即i/2

是i

的双亲;i

为偶数i

为奇数如果i为右孩子,且i

的双亲为p,则有i=2p+1,

p=

(i-1)/2=i/2–1/2=i/2,即i/2

是i

的双亲。证毕。6.2.3二叉树的存储结构1、顺序存储结构

完全二叉树:用一组地址连续的存储单元依次自上而下、自左至右存储结点元素,即将编号为i

的结点元素存储在一维数组中下标为i–1的分量中。123456123450

6一般二叉树:将其每个结点与完全二叉树上的结点相对照,存储在一维数组的相应分量中。

这种顺序存储结构仅适用于完全二叉树245361完全二叉树245361非完全二叉树123456最坏情况:深度为k

的且只有k

个结点的右单支树需要长度为2k-1的一维数组。

1

0

20003表示方式:

#defineMAX_TREE_SIZE100//二叉树的最大结点数

typedef

TElemType

SqBiTree[MAX_TREE_SIZE];//

0号单元存储根结点

SqBiTree

bt;231右单支树00002、链式存储结构存储方式二叉树结点的特点lchilddatarchild结点结构datarchild

lchild

data

parentlchildrchildAB

CD

E

F

G^^^^^^^^AB

CD^^^^^二叉链表在n

个结点的二叉链表中,有

n+1个空指针域。ABCDEFGABCDtypedef

struct

BiTNode{//结点结构

TElemTypedata;

struct

BiTNode*lchild,*rchild;//左右孩子指针}BiTNode,*BiTree;C语言的类型描述如下:

ABCDEF

G^^^^^^^^^三叉链表ABCDEFG▲parentdatalchild

rchild

lchilddataparentrchild

结点结构6.3遍历二叉树和线索二叉树6.3.1遍历二叉树(重点,是进行其他运算的基础)遍历概念顺着某一条搜索路径巡访二叉树中的结点,使得每个结点均被访问一次,而且仅被访问一次。

“访问”的含义很广,可以是对结点作各种处理,如:输出结点的信息、修改结点的数据值等,但要求这种访问不破坏原来的数据结构。遍历方法依次遍历二叉树中的三个组成部分,便是遍历了整个二叉树假设:L:遍历左子树

D:访问根结点

R:遍历右子树则遍历整个二叉树方案共有:遍历目的得到树中所有结点的一个线性排列。根结点

左子树右子树DLR、LDR、LRD、DRL、RDL、RLD六种。若规定先左后右,则只有前三种情况:

DLR——先(根)序遍历,

LDR——中(根)序遍历,

LRD——后(根)序遍历。根结点

左子树右子树

先序遍历二叉树的操作定义:

若二叉树为空,则空操作;否则

(1)访问根结点;

(2)先序遍历左子树;

(3)先序遍历右子树。先序遍历的顺序为:ABC先序遍历的顺序为:ABELDHMIJA

B

C

A

B

D

E

LH

M

I

J

中序遍历二叉树的操作定义:

若二叉树为空,则空操作;否则

(1)中序遍历左子树;

(2)访问根结点;

(3)中序遍历右子树。中序遍历的顺序为:BAC中序遍历的顺序为:ELBAMHIDJA

B

C

A

B

D

E

LH

M

I

J

后序遍历二叉树的操作定义:

若二叉树为空,则空操作;否则

(1)后序遍历左子树;

(2)后序遍历右子树;

(3)访问根结点。后序遍历的顺序为:BCA后序遍历的顺序为:LEBMIHJDA

A

B

C

A

B

D

E

LH

M

I

J

--/+×abcdef例:请写出下图所示二叉树的先序、中序和后序遍历顺序。遍历结果:先序:-+a×b

-

cd/ef

中序:a+b×c

-

d

-

e/f后序:abcd

-×+ef/-表达式的前缀表示(波兰式)表达式的中缀表示

表达式的后缀表示(逆波兰式)先序遍历二叉树基本操作的递归算法在二叉链表上的实现:StatusPreOrderTraverse(BitreeT,Visit){//最简单的Visit函数是:

//StatusPrintElement(TElemTypee)//{Printf(e);//实用时加上格式串。

//returnOK;}

//调用实例:PreOrderTraverse(T,PrintElement);

if(T){if(Visit(Tdata))if(PreOrderTraverse(Tlchild,Visit))if(PreOrderTraverse(Trchild,Visit))returnOK;returnERROR;}elsereturnOK;}//PreOrderTraverse

lchilddatarchild结点结构AB

CD

E

F

G^^^^^^^^T中序遍历二叉树基本操作的递归算法在二叉链表上的实现:StatusInOrderTraverse(BitreeT,Visit){if(T){if(InOrderTraverse(Tlchild,Visit))if(Visit(Tdata))if(InOrderTraverse(Trchild,Visit))returnOK;returnERROR;}elsereturnOK;}//InOrderTraverse

AB

CD

E

F

G^^^^^^^^T后序遍历二叉树基本操作的递归算法在二叉链表上的实现:StatusPostOrderTraverse(BitreeT,Visit){if(T){if(PostOrderTraverse(Tlchild,Visit))if(PostOrderTraverse(Trchild,Visit))if(Visit(Tdata))

returnOK;returnERROR;}elsereturnOK;}//PostOrderTraverse

AB

CD

E

F

G^^^^^^^^T以中序遍历为例来说明中序遍历二叉树的递归过程第一次经过第二次经过第三次经过Ф

BDФ

Ф

ABCED▲中序遍历二叉树基本操作的非递归算法在二叉链表上的实现:StatusInOrderTraverse(BiTreeT,Visit){InitStack(S);Push(S,T);//根指针进栈

while(!StackEmpty(S){while(GetTop(S,p)&&p)Push(S,plchild);//向左走到尽头。

Pop(S,p);//空指针退栈

if(!StackEmpty(S){//访问结点,向右一步

Pop(S,p);if(!Visit(pdata))returnERROR;Push(S,prchild);}//if}//whilereturnOK;}//InOrderTraverse-+

a

^^×

b

^^-

c

^^

d

^^/

e

^^

f

^^--/+×abcdefTpppa+

c-

d-

e/

f中序遍历二叉树基本操作的非递归算法在二叉链表上的实现:StatusInOrderTraverse(BiTreeT,Status(*Visit)(TElemTypee)){InitStack(S);p=T;while(p||!StackEmpty(S)){if(p){push(S,p);p=plchild;}//根指针进栈,遍历左子树。

else{//根指针退栈,访问根结点,遍历右子树。

Pop(S,p);if(!Visit(pdata))returnERROR;p=prchild;}//else}//whilereturnOK;}//InOrderTraverse-+

a

×

b

c

d

e

f

--/+×abcdefT二叉树其它操作算法举例统计二叉树中叶子结点的个数实现这个操作只要对二叉树“遍历”一遍,并在遍历过程中对“叶子结点计数”即可。显然这个遍历的次序可以随意,只是为了在遍历的同时进行计数,需要在算法的参数中设一个“计数器”。voidCountLeaf(BiTreeT,int&count)

{//先序遍历二叉树,以count返回二叉树中叶子结点的数目

if(T){

if((!TLchild)&&(!TRchild))//无左、右子树

count++;

//对叶子结点计数

CountLeaf(TLchild,count);

CountLeaf(TRchild,count);

}//if

}//CountLeaf

问题:能否将count设成函数中的局部变量,然后以count的值作为函数值返回?答案:不能!

原因:算法需要在递归执行的过程中对叶子“累加计数”。求二叉树的深度(先序)二叉树的深度=叶子结点所在层次的最大值。voidBiTreeDepth(BiTreeT,intlevel,int&depth)

{//level为T所指结点所在层次,其初值为1,

//depth为当前求得的最大层次,其初值为0。

if(T){

if(level>depth)depth=level;

BiTreeDepth(TLchild,level+1,depth);

BiTreeDepth(TRchild,level+1,depth);

}//if

}//BiTreeDepth^^^^^^^BCDEGFT求二叉树的深度(后序)二叉树的深度=MAX(左子树深度,右子树深度)+1。voidBiTreeDepth(BiTreeT)

{if(!T)depth=0;else{

depthleft=BiTreeDepth(TLchild);

depthright=BiTreeDepth(TRchild);depth=max(depthleft,depthright)+1;

}returndepth;

}//BiTreeDepth

^^^^^^^BCDEGFT^建立二叉树的存储结构——二叉链表(先序)StatusCreateBiTree(BiTree&T){

scanf(&ch);

if(ch==‘’)T=NULL;

else{

if(!(T=(BiTNode*)malloc(sizeof(BiTNode))))

exit(OVERFLOW);

Tdata=ch;//生成根结点

CreateBiTree(Tlchild);//构造左子树

CreateBiTree(Trchild);//

构造右子树

}

returnOK;

}//CreateBiTree

^^^^^^^DABEFGC

对右图所示二叉树,按下列顺序读入字符:

ABCDEGFABCDEGF▲ABCDEGFT6.3.2线索二叉树遍历结果:先序:-+a×b

-

cd/ef

中序:a+b×c

-

d

-

e/f后序:abcd

-×+ef/-问题1:为什么要研究线索二叉树?

非线性结构线性结构转化问题2:如何保存结果以避免重复遍历?办法:

1、另辟存储空间存放遍历结果。

需要付出额外的存储花销。

--/+×abcdef遍历结果:先序:-+a×b

-

cd/ef

中序:a+b×c

-

d

-

e/f后序:abcd

-×+ef/-2、在原二叉链表的每个结点上增加两个指针域。-+

a

×

b-

c

d/

e

f000000001111001111111101线索化:对二叉树按某种次序遍历使其变为线索二叉树的过程。二叉树线索化的目的:利用线索化后的二叉树中的线索就可以直接找到某些结点在某种遍历序列中的前趋和后继结点。

3、在原二叉链表的存储空间内反映遍历结果。中序线索链表--/+×abcdef中序线索二叉树thrt

二叉树线索化的实质:在遍历过程中用线索取代空指针。

在线索树(中序)中找结点后继的方法:

1、若右链是线索,则直接指示后继;

2、若右链是指针,则“右孩找左”。即:中序后继右孩找左。在线索树(中序)中找结点前驱的方法:

1、若左链是线索,则直接指示前驱;

2、若左链是指针,则“左孩找右”。即:中序前驱左孩找右。在线索树上进行遍历的方法:

1、从序列中的第一个结点起,依次找后继,直至后继为空。

2、从序列中的最后一个结点起,依次找前驱,直至前驱为空。

线索二叉树的存储表示typedef

enum

PointerTag

{Link,Thread};

//Link==0:指针,Thread==1:线索

typedef

struct

BiThrNode{

TElemTypedata;

struct

BiThrNode*lchild,*rchild;//左右指针

PointerTag

LTag,RTag;//左右标志

}BiThrNode,*BiThrTree;线索链表的遍历算法(中序找后继法):StatusInOrderTraverse_Thr(BiThrTreeT,Visit){p=T->lchild;

while(p!=T)

{while(p->LTag==0)p=p->lchild;

if(!Visit(p->data))returnERROR;

while(p->

RTag==1&&p->

rchild!=T)

{p=p->

rchild;Visit(p->data);}

p=p->

rchild;}

returnOK;

}//InOrderTraverse_Thr

-+

a

×

b-

c

d/

e

f000000001111001111111101T

按中序建立线索链表(按中序线索化二叉树)

1、若结点没有左子树,则令其左指针指向它的“前驱”并将左指针类型标志改为“1”;

2、若结点没有右子树,则令其右指针指向它的“后继”并将右指针类型标志改为“1”;

3、为了获取“前驱”的信息,需要在遍历过程中添加一个指针

pre,令其始终指向刚刚访问过的结点。若指针p指向当前访问的结点,则pre指向它的前驱。-+

a

×

b-

c

d/

e

fT

StatusInOrderThreading(BiThrTree&Thrt,BiThrTreeT){

if(!(Thrt=(BiThrTree)malloc(sizeof(BiThrNode))))exit(OVERFLOW);

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;}

returnOK;

}//InOrderThreadingvoidInThreading(BiThrTreep){

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);}

//右子树线索化

}

}//InThreading

-+

a

×

b-

c

d/

e

f111111111111T

0Thrt

pre

▲StatusInOrderThreading(BiThrTree&Thrt,BiThrTreeT){

if(!(Thrt=(BiThrTree)malloc(sizeof(BiThrNode))))exit(OVERFLOW);

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;}

returnOK;

}//InOrderThreading-+

a

×

b-

c

d/

e

f1111111111111T

0Thrt

pre

作业:6.15~6.17选择题1.在线索化二叉树中,t所指结点没有左子树的充要条件是()(A)t->lchild=NULL(B)t->ltag=1(C)t->ltag=1且t->lchild=NULL(D)以上都不对二叉树按某种顺序线索化后,任一结点均有指向其前趋和后继的线索,这种说法()(A)正确(B)错误实现:定义结构数组存放树的结点,每个结点含两个域:数据域:存放结点本身信息。双亲域:指示本结点的双亲结点在数组中的位置。6.4树和森林6.4.1树的存储结构

1双亲表示法

parent0R-11A02B03C04D15E1F3G6H6K6数组下标特点:找双亲容易,找孩子难。r=0n=10RBACDEFHGKdatatypedef

struct

PTNode{

TElemTypedata;

intparent;//双亲位置域

}PTNode;#defineMAX_TREE_SIZE100C语言的类型描述:

dataparent结点结构:typedef

struct{

PTNodenodes[MAX_TREE_SIZE];

intr,n;//根结点的位置和结点个数

}PTree;树结构:0R-11A02B03C04D15E1F3G6H6K6dataparent数组下标r=0n=10lchild

data

rchildlchilddataparent

rchild

datachild1child2……childd

datachild1child2……childd

parent二叉树的结点结构data

parentlchildrchilddata

parentchild1

childd

……2孩子表示法(树的链式存储结构)

1)多重链表d

叉链表AB^^^C^^^D^E^^^F^^^

结点同构:结点的指针个数相等,为树的度

d。ABCDEFdatachild1child2……childd

d+1叉链表多重链表在有n

个结点、度为

d

的树的d

叉链表中,有

n×(d-1)+1个空链域。

datachild1child2……childd

parent

A3B0C0D2E0F0节约存储空间但操作不方便ABCDEFdatadegreechild1child2……childd

datadegreechild1child2……childd

parent

结点不同构:结点的指针个数不相等,

为该结点的度degree。A3^B0C0D2E0F0孩子链表:把每个结点的孩子结点排列起来,看成是一个线性表,用单链表存储,则n

个结点有n

个孩子链表(叶子的孩子链表为空表)。而n

个头指针又组成一个线性表,用顺序表(含

n

个元素的结构数组)存储。

0A1B^2C3D^4R5E^FG^H^K^datafirstchild

数组下标r=4n=10

3

5^

6^

0

1

2^

7

8

9^4440-102666特点:找孩子容易,找双亲难。带双亲的孩子链表RBACDEFHGK孩子链表typedef

struct

CTNode{

intchild;

struct

CTNode*next;}*ChildPtr;孩子结点结构:childnextC语言的类型描述:

typedef

struct{

TElemTypedata;

ChildPtr

firstchild;//孩子链表头指针}CTBox;双亲结点结构:

datafirstchild

typedef

struct{

CTBoxnodes[MAX_TREE_SIZE];

intn,r;//结点数和根结点的位置}CTree;树结构:RBACDEFHGK3孩子兄弟表示法(二叉树表示法,二叉链表表示法)实现:用二叉链表作树的存储结构,链表中每个结点的两个指针域分别指向其第一个孩子结点和下一个兄弟结点

R^

A^

D^

B^

E^

C^

F^^

G^

H^

K^孩子兄弟链表的结构形式与二叉链表完全相同,但存储结点中指针的含义不同:二叉链表中结点的左右指针分别指向该结点的左右孩子;而孩子兄弟链表结点的左右指针分别指向它的“长子”和“大弟”。注这种解释上的不同正是树与二叉树相互转化的内在基础typedef

s

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论