数据结构之二叉树
一、二叉树的定义
定义:
二叉树是一个有限的结点集合,这个集合或者为空,或者由一个根节点和两棵互不相交的称为左子树和右子树的二叉树组成。
满二叉树:
- 在一棵二叉树中,如果所有分支节点都有左右孩子结点,并且叶子结点都集中在二叉树的最下一层,这样的二叉树称为满二叉树。
`` 2. 非空满二叉树特点:
(1)叶子结点都在最下一层。
(2)只有度为0和度为2的结点。
完全二叉树:
- 若二叉树中最多只有最下面两层的结点的度数可以小于2,并且最下面一层的叶子结点都依次排列在该层最左边的位置上,这样的二叉树称为完全二叉树。
2.非空二叉树特点:
(1)叶子结点只可能在最下面两层中出现。
(2)对于最大层次中的叶子结点,都依次排列在该层最左边的位置上。
(3)如果有度为1的结点,只可能有一个,且该结点只有左孩子而无右孩子。
(4)在按层序编号时,一旦出现编号为i的结点是叶子结点或只有左孩子,则编号大i 的结点均为叶子结点。
(5)当结点总数 n为奇数时,n₁=0,当结点总数n 为偶数时,n₁=1。
二、二叉树的性质
性质1:非空二叉树上的叶子结点数等于双分支结点数加1。
性质2:非空二叉树的第i层上最多有2ⁱ⁻¹个结点。(i≥1)。
性质3:高度为h的二叉树最多有2ʰ−1个结点。(h≥1)。
性质4:完全二叉树中层序编号为 i 的结点(1≤i≤n,n≥1,n为结点数)有以下性质。
(1)若i≤[n/2⌋,即2i≤n,则编号为 i的结点为分支结点,否则为叶子结点。
(2)若 n为奇数,则每个分支结点都既有左孩子结点,又有右孩子结点;若n为偶数,则编号最大的分支结点(编号为[n/2])只有左孩子结点,没有右孩子结点,其余分支结点都有左、右孩子结点。
(3)若编号为i的结点有左孩子结点,则左孩子结点的编号为2i;若编号为i的结点有右孩子结点,则右孩子结点的编号为2i+1。
(4)除根结点以外,若一个结点的编号为i,则它的双亲结点的编号为[i/2]
性质 5:具有n个(n>0)结点的完全二叉树的高度为[log₂(n+1)]或[log₂n]+1
三、二叉树的基本运算算法的实现
1.创建二叉树:CreateBTree(*b,*str)
假设采用括号未示法表系的二叉村宇行中 str 是正确的。用ch海5s,其中只有么安宇符,其处理方式如下。
- 若ch='(',表示前面刚创建的结点p存在孩子结点,需要将其进栈,以便建立它和它的孩子结点之间的关系(如果一个结点刚创建完毕,其后一个字符不是'(',表示该结点是叶子结点,不需要进栈)。然后开始处理该结点的左孩子,置k=1(表示其后创建的结点将作为当前栈顶结点的左孩子结点)。
- 若ch=')',表示以栈顶结点为根结点的子树创建完毕,将其退栈。
- 若ch=',',表示开始处理栈顶结点的右孩子结点,置k=2(表示其后创建的结点将作为当前栈顶结点的右孩子结点。
- 其他情况:只能是单个宇符,对应二叉树中的某个结点值,需要创建一个结点p存放该结点值。根据k值建立它与栈顶结点之间的联系。当k=2时,将结点p作为栈顶结点的左孩子;当k=2时,将结点p作为栈顶结点的右孩子。
如此循环,直到 str遍历完毕。在算法中使用一个栈保存双亲结点,为了简单用数组St表示栈,top 为栈项指针,k指定其后处理的结点是双亲结点(栈顶结点)的左孩子(k=1)还是右孩子(k=2)。
对应的算法如下:
#include"btree.h"
void CreateBTree(BTNode * &b,char * str)
{ BTNode St[MaxSize],*P;
int top=-1,k,j=0;
char ch;
b=NULL;
ch=str[j];
while(ch!='\0')
{ switch(ch)
{
case'(':top++;St[top]=p;k=1;break;
case')’:top--;break;
case',':k=2; break;
defeult:p=(BTNode*)malloc(sizeof(BTNode));
p -> data=ch;
p -> lchild=p -> rchild=NULL;
if(b==NULL)
b=p;
else
{ switch(k)
{
case 1:St[top] -> lchild=p;break; case 2:St[top] -> rchild=p;break;
}
}
}
j++;
ch=str[j];
}
}
2.销毁二叉树:DestoryBTree(&b)
设f(b)的功能是释放为二叉树b中的所有结点分配的空间。其递归模型如下:
f(b)≡不做任何事情
f(b)≡f(b -> lchild);f(b -> rchild);
对应的递归算法如下:
void DestroyBTree( BTNode * &b)
{ if(b!=NULL)
{ DestroyBTree(b -> lchild);
DestroyBTree(b -> rchild);
free(b);
}
}
3.查找结点:FindNode(b,x)
设f(b.x)的功能是在二叉树b中查找值为X的结点,找到后返回其地址,否则返回NULL。其递归模型如下:
f(b,x) = NULL
f(b,x) = b
f(b,x) = p
f(b,x) = f(b -> rchild,x)
对应的递归算法如下:
BTNode * FindNode(BTNode * b,ElemType x)
{ BTNode * p;
if(b==NULL)
return NULL;
else if ( b -> data == x)
return b;
else
{ p=FindNode(b -> lchild,x);
if(p!=NULL)
return p;
else
return FindNode(b -> rchild,x);
}
}
4.找孩子结点:LchildNode(p)和 RchildNode(p)
其用于直接返回结点p的左孩子或右孩子结点地址。算法如下:
BTNode * LchildNode(BTNode * p)
{
return p -> lchild;
}
BTNode * RchildNode(BTNode * p)
{
return p -> rchild;
}
5.求高度:BTHeight(b)
求二叉树b的高度的递归模型f(b)如下:
f(b) = 0
f(b) = MAX{f(b -> lchild),f(b -> rchild)}+1
对应的递归算法如下:
int BTHeight(BTNode * b)
{ int lchildh, rchildh;
if(b==NULL) return(0);
else
{ lchildh=BTHeight(b -> lchild);
rchildh=BTHeight(b -> rchild);
return (lchildh > rchildb) ? (lchildh+1):(rchildh+1);
}
}
6.输出二叉树:DispBTree(b)
其过程是对于非空二叉树b,先输出结点b的结点值,当它存在左孩子或右孩子时输出一个"("符号,然后递归输出左子树;当存在右孩子时,输出一个","符号,再递归输出右子树,最后输出一个")"符号。对应的递归算法如下:
void DispBTree(BTNode * b)
{ if (b!=NULL)
{ printf("%c",b -> data);
if (b -> lchild!=NULL || b -> rchild!=NULL)
{ printf("( ");
DispBTree(b -> lchild);
if (b -> rchild!=NULL) printf(",");
DispBTree(b -> rchild);
printf(")");
}
}
}