第6章 树和二叉树Word格式文档下载.docx
《第6章 树和二叉树Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《第6章 树和二叉树Word格式文档下载.docx(14页珍藏版)》请在冰豆网上搜索。
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
完全二叉树:
如果一棵二叉树,它的编号与满二叉树完全一致。
其特点:
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、顺序存储结构
用一个数组来存储,如:
可得到:
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;
rchild);
rchild;
}
returnOK;
}
对于非递归方式,需要用栈。
StatusPreOrderTraverse_2(BiTree*T)//非递归方式前序遍历二叉树T
InitStack(S);
if(T==NULL)returnERROR;
//如果T本身是空树,无法访问
Push(S,T);
//根入栈
while(!
EmptyStack(S))//如果栈不空,继续循环
Pop(S,p);
printf(p->
//访问p
if(p->
rchild!
=NULL)//如果p的左孩子不空,则入栈
Push(S,p->
lchild!
2、中序(又称中根),它的进程是:
1)先用中序方式遍历其左子树;
2)再访问它;
3)再用中序方式遍历其右子树;
以T1为例,可得到顺序:
E、L、B、A、O、H、D、J
算法的实现,仍然有递归与非递归方式,递归方式:
StatusInOrderTraverse(BiTree*T)//递归方式中序遍历二叉树T
InOrderTraverse(T->
//再以中序方式访问T->
非递归算法:
StatusInOrderTraverse_2(BiTree*T)//非递归方式中序遍历二叉树T
p=T;
EmptyStack(S)||p!
while(p!
=NULL)//p的所有的左子树依次入栈
{
Push(S,p);
p=p->
}
if(!
EmptyStack(S))
Pop(S,p);
printf(p->
E、L、B、A
3、后序(又称后根),它的进程是:
1)先用后序方式遍历其左子树;
2)再用后序方式遍历其右子树;
3)再访问它;
StatusPostOrderTraverse(BiTree*T)//递归方式后序遍历二叉树T
PostOrderTraverse(T->
//再以后序方式访问T->
//访问T;
4、层次遍历,它的进程是从根开始,从上到到,从左至右依次访问:
以T1为例,可得顺序:
A、B、D、E、H、J、L、O
其算法可用非递归实现,借助队列来实现。
StatusLevelOrderTraverse(BiTree*T)//层次遍历二叉树T
if(T==NULL)returnERROR;
InitQueue(Q);
EnQueue(Q,T);
EmptyQueue(Q))
DeQueue(Q,p);
//出队
lchild!
EnQueue(Q,p->
rchild!
5、补充算法:
如何交换一棵二叉树的所有左、右孩子
有递归和非递归的算法,下面先看递归:
递归的思想与前序遍历很相似,每访问一个结点,就将该结点的左右孩子进行交换,再用递归对其左子树进行交换、右子树进行交换:
StatusExchangeBiTree(BiTree&
T)
s=T->
T->
lchild=T->
rchild=s;
//交换T的左右孩子;
ExchangeBiTree(T->
//再调用函数,交换其左子树;
//再调用函数,交换其右子树;
非递归算法,与前序遍历又非常相似。
StatusExchangeBiTree_2(BiTree&
p->
lchild<
->
p->
if(p->
=NULL)Push(S,p->
lchild);
rchild);
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;
CreateBiTree(T->