第6章 树和二叉树.docx

上传人:b****6 文档编号:5966503 上传时间:2023-01-02 格式:DOCX 页数:14 大小:144.16KB
下载 相关 举报
第6章 树和二叉树.docx_第1页
第1页 / 共14页
第6章 树和二叉树.docx_第2页
第2页 / 共14页
第6章 树和二叉树.docx_第3页
第3页 / 共14页
第6章 树和二叉树.docx_第4页
第4页 / 共14页
第6章 树和二叉树.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

第6章 树和二叉树.docx

《第6章 树和二叉树.docx》由会员分享,可在线阅读,更多相关《第6章 树和二叉树.docx(14页珍藏版)》请在冰豆网上搜索。

第6章 树和二叉树.docx

第6章树和二叉树

第6章树和二叉树

6.1树的定义和基本术语

树(Tree)是n(n≥0)个结点的有限集。

在任意一棵非空树中:

1)有且仅有一个特定的称为根(Root)的结点;2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2,……,Tm,其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)。

其中A是整棵树的根,而B、C、D等就是A的子树。

结点:

结点的度:

以该结点为根的子树的数目,如D有三棵子树,D的度为3.

树的度:

一棵树中,某个结点的度最大就是树的度。

叶子:

又称终端结点,指没有孩子的结点。

如K、L、F等。

非终端结点:

有孩子的结点。

如E、C、D等。

孩子:

结点子树的根称为孩子。

双亲(有时称父结点,有时又称母结点):

兄弟:

同一个双亲的孩子之间互称为兄弟,如H、I、J都是D的孩子,那么H、I、J互称为兄弟。

祖先:

D和A是M的祖先。

子孙:

E、F、K等都是A的子孙。

层次:

从根开始称为第1层,根的孩子为第2层,如此之般。

堂兄弟:

如果双亲在同一层的结点互为堂兄弟。

如:

M与K、L互称为堂兄弟。

深度:

树中结点的最大层次称为树的深度。

上图深度为4,又称为高度。

有序树:

如果一棵树的每个结点,其若干个孩子中,左右顺序不能互换,称为有序树。

无序树:

在有序树中,最左边的那个孩子称为第一个孩子,最右边的那个称为最后一个孩子。

森林:

由若干棵(0或1或多棵)互不相交的树构成的。

6.2二叉树

6.2.1二叉树的定义

二叉树(BinaryTree),

是一棵特殊的树:

每个结点的度都不超过2(0、1、2),且是有序树。

其左右孩子不能交换位置。

二叉树有5种基本形态:

题目:

已知一棵二叉树有3个结点,问有几种可能形态?

思考:

有n个结点的二叉树有几种可能的形态?

答:

种形态

6.2.2二叉树的性质

性质1:

在二叉树的第i层上至多有2i-1个结点(i>=1)

性质2:

深度为k的二叉树至多有2k-1个结点。

性质3:

对任何一棵二叉树T,如果其终端(叶子)结点数为n0,度为2的结点数为n2,则有公式:

n0=n2+1

证明:

一方面,结点的总数n=n0+n1+n2

另一方面:

结点的总数n=B+1,而边数B=n2*2+n1*1+n0*0

所以:

n0+n1+n2=2n2+n1+1

n0=n2+1

完全二叉树:

如果一棵二叉树,它的编号与满二叉树完全一致。

其特点:

1)叶子只可能在最后两层上;

2)对任一结点,若其右分支下的子孙的最大层次为l,则左分支下的子孙最大层次必为l或l+1。

1

满二叉树:

如果有一棵二叉树,它的每一层的结点个数恰好为最多的可能个数,也就是第i层上有2i-1个结点,这样的二叉树称为满二叉树。

形象地说,就是一棵二叉树满满的。

对于满二叉树,可以对结点进行编号,从上往下,从左往右进行。

性质4:

具有n个结点的完全二叉树的深度为

(向下取整为不大于该数的最大整数,如2.69结果为2,类似的还有向上取整,

取不小于该数的最小整数,如

=3)

证明:

设该树的深度为k,根据性质2及完全二叉树的定义有:

性质5:

对于一棵有n个结点的完全二叉树,如果按照上面的编号,则:

1)如果i=1,则结点i是根,它没有双亲;如果i>1,则它有双亲,其双亲结点编号为

2)如果i很大,2i>n,则结点i无左孩子,且该结点为叶子;否则,如果i不大,2i<=n,则该结点有左孩子,且左孩子的编号为2i。

3)如果i很大,2i+1>n,则结点i无右孩子;否则,如果i不大,2i+1<=n,则该结点有右孩子,且右孩子的编号为2i+1。

6.2.3二叉树的存储结构

1、顺序存储结构

1

用一个数组来存储,如:

可得到:

6

一般用在满二叉树或完全二叉树中,比较方便,后面的排序一章中的堆排序就是用了这种方法。

2、链式存储结构:

typedefstructBiTNode{

TElemTypedata;

structBiTNode*lchild;

structBiTNode*rchild;

}BiTNode,*BiTree;

优点:

1)基本不会浪费空间;2)找左孩子与右孩子很方便;

缺点:

1)找双亲结点很困难;2)用来存储满二叉树会浪费空间(主要是指针本身需要空间。

二叉树的基本操作:

1)创建二叉树

2)几个遍历操作:

前序、中序、后序、层次

3)插入一个结点

4)删除一个结点

5)其它操作

6.3遍历二叉树和线索二叉树

6.3.1遍历二叉树

所谓遍历,就是按照某种顺序对二叉树中的所有的结点依个去访问,且只访问一次。

对于二叉树,有四种遍历方法:

前序、中序、后序、层次

1、前序(又称前根、先根、先序),它的进程是:

1)先访问根;

2)再以前序方式来访问该结点的左子树

3)再以前序方式来访问该结点的右子树

以二叉树T1为例:

1)先访问根A;

2)再以前序方式访问A的左子树(以B为根的二叉树);

2.1先访问根B;

2.2再以前序访问B的左子树(以E为根的二叉树);

2.2.1先访问根E;

2.2.2再以前序访问E的左子树(空),直接返回;

2.2.3再以前序访问E的右子树(以L为根的二叉树);

2.2.3.1先访问根L;

2.2.3.2再以前序访问L的左子树(空),直接返回;

2.2.3.2再以前序访问L的右子树(空),直接返回;

2.3再以前序访问B的右子树(空),直接返回;

3)再以前序方式访问A的右子树(以D为根的二叉树)

……

可等到遍历序列:

A、B、E、L、D、H、O、J

实现的算法有两种:

递归方式、非递归方式

先看递归方式:

StatusPreOrderTraverse(BiTree*T)//递归方式前序遍历二叉树T

{

if(T!

=NULL)

{

printf(T->data);//访问T;

PreOrderTraverse(T->lchild);//再以前序方式访问T->lchild;

PreOrderTraverse(T->rchild);//再以前序方式访问T->rchild;

}

returnOK;

}

对于非递归方式,需要用栈。

A、B、E、L、D、H、O、J

StatusPreOrderTraverse_2(BiTree*T)//非递归方式前序遍历二叉树T

{

InitStack(S);

if(T==NULL)returnERROR;//如果T本身是空树,无法访问

Push(S,T);//根入栈

while(!

EmptyStack(S))//如果栈不空,继续循环

{

Pop(S,p);

printf(p->data);//访问p

if(p->rchild!

=NULL)//如果p的左孩子不空,则入栈

Push(S,p->rchild);

if(p->lchild!

=NULL)

Push(S,p->lchild);

}

returnOK;

}

2、中序(又称中根),它的进程是:

1)先用中序方式遍历其左子树;

2)再访问它;

3)再用中序方式遍历其右子树;

以T1为例,可得到顺序:

E、L、B、A、O、H、D、J

算法的实现,仍然有递归与非递归方式,递归方式:

StatusInOrderTraverse(BiTree*T)//递归方式中序遍历二叉树T

{

if(T!

=NULL)

{

InOrderTraverse(T->lchild);//再以中序方式访问T->lchild;

printf(T->data);//访问T;

InOrderTraverse(T->rchild);//再以中序方式访问T->rchild;

}

returnOK;

}

非递归算法:

StatusInOrderTraverse_2(BiTree*T)//非递归方式中序遍历二叉树T

{

InitStack(S);

p=T;

if(T==NULL)returnERROR;//如果T本身是空树,无法访问

while(!

EmptyStack(S)||p!

=NULL)

{

while(p!

=NULL)//p的所有的左子树依次入栈

{

Push(S,p);

p=p->lchild;

}

if(!

EmptyStack(S))

{

Pop(S,p);

printf(p->data);

p=p->rchild;

}

}

returnOK;

}

E、L、B、A

3、后序(又称后根),它的进程是:

1)先用后序方式遍历其左子树;

2)再用后序方式遍历其右子树;

3)再访问它;

StatusPostOrderTraverse(BiTree*T)//递归方式后序遍历二叉树T

{

if(T!

=NULL)

{

PostOrderTraverse(T->lchild);//再以后序方式访问T->lchild;

PostOrderTraverse(T->rchild);//再以后序方式访问T->rchild;

printf(T->data);//访问T;

}

returnOK;

}

4、层次遍历,它的进程是从根开始,从上到到,从左至右依次访问:

以T1为例,可得顺序:

A、B、D、E、H、J、L、O

其算法可用非递归实现,借助队列来实现。

A、B、D、E、H、J、L、O

StatusLevelOrderTraverse(BiTree*T)//层次遍历二叉树T

{

if(T==NULL)returnERROR;

InitQueue(Q);

EnQueue(Q,T);

while(!

EmptyQueue(Q))

{

DeQueue(Q,p);//出队

printf(p->data);

if(p->lchild!

=NULL)

EnQueue(Q,p->lchild);

if(p->rchild!

=NULL)

EnQueue(Q,p->rchild);

}

returnOK;

}

5、补充算法:

如何交换一棵二叉树的所有左、右孩子

有递归和非递归的算法,下面先看递归:

递归的思想与前序遍历很相似,每访问一个结点,就将该结点的左右孩子进行交换,再用递归对其左子树进行交换、右子树进行交换:

StatusExchangeBiTree(BiTree&T)

{

if(T!

=NULL)

{

s=T->lchild;T->lchild=T->rchild;T->rchild=s;//交换T的左右孩子;

ExchangeBiTree(T->lchild);//再调用函数,交换其左子树;

ExchangeBiTree(T->rchild);//再调用函数,交换其右子树;

}

returnOK;

}

非递归算法,与前序遍历又非常相似。

StatusExchangeBiTree_2(BiTree&T)

{

if(T==NULL)returnERROR;

InitStack(S);

Push(S,T);

while(!

EmptyStack(S))

{

Pop(S,p);

p->lchild<->p->rchild;

if(p->lchild!

=NULL)Push(S,p->lchild);

if(p->rchild!

=NULL)Push(S,p->rchild);

}

returnOK;

}

6、补充算法:

二叉树在数学表达式计算中的应用

在栈与队列一章中,介绍过用栈来计算数学表达式。

如:

a+b*(c-d)-e/f,的中缀表示,它用中序遍历可得:

a+b*c-d-e/f,如果用后序遍历,可得:

abcd-*+ef/-,又称为后缀表示,也称为逆波兰式。

如果用前序遍历:

-+a*b-cd/ef,又称为前缀表示(波兰式)。

(a*(b+c/(d-e)-f)+g)*l

7、创建二叉树

可用递归与非递归的方法,对于递归算法:

用户如果输入:

ABE#L###DHO###J##,则可得二叉树:

StatusCreateBiTree(BiTree&T,char*str)

{

staticchar*ch=str;

if(ch=='#'){T=NULL;ch++;}

else

{

T=(BiTNode*)malloc(sizeof(BiTNode));

T->data=ch;

ch++;

CreateBiTree(T->lchild);

CreateBiTree(T->rchild);

}

returnOK;

}

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 其它

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1