一,二叉树的定义
二叉树(Binary tree)是n(n≥0)个结点的有限集合。若n=0时称为空树,否则:
⑴有且只有一个特殊的称为树的根(Root)结点;
⑵若n>1时,其余的结点被分成为二个互不相交的子集T1,T2,分别称之为左、右子树,并且左、右子树又都是二叉树。
由此可知,二叉树的定义是递归的。
二叉树在树结构中起着非常重要的作用。因为二叉树结构简单,存储效率高,树的操作算法相对简单,且任何树都很容易转化成二叉树结构。
二,二叉树的基本形态
三,二叉树的性质
性质1:在非空二叉树中,第i层上至多有2i-1个结点(i≧1)。
性质2:深度为k的二叉树至多有2k-1个结点(k≧1)
性质3:对任何一棵二叉树,若其叶子结点数为n0,度为2的结点数为n2,则n0=n2+1。
证明:设二叉树中度为1的结点数为n1,二叉树中总结点数为N,因为二叉树中所有结点均小于或等于2,则有:N=n0+n1+n2
再看二叉树中的分支数:
除根结点外,其余每个结点都有唯一的一个进入分支,而所有这些分支都是由度为1和2的结点射出的。设B为二叉树中的分支总数,则有: N=B+1
∴ B=n1+2*n2
∴ N=B+1=n1+2*n2+1
∴ n0+n1+n2=n1+2*n2+1
即 n0=n2+1
四,满二叉树和完全二叉树
一棵深度为k且有 2^k -1 个结点的二叉树称为满二叉树(Full Binary Tree)。
满二叉树的特点:
◆ 基本特点是每一层上的结点数总是最大结点数。
◆ 满二叉树的所有的支结点都有左、右子树。
◆ 可对满二叉树的结点进行连续编号,若规定从根结点开始,按“自上而下、自左至右”的原则进行。
完全二叉树(Complete Binary Tree):如果深度为k,由n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1到n的结点一一对应,该二叉树称为完全二叉树。 或深度为k的满二叉树中编号从1到n的前n个结点构成了一棵深度为k的完全二叉树。 其中 2^(k-1 )≦n≦2^k-1 。
完全二叉树是满二叉树的一部分,而满二叉树是完全二叉树的特例。
完全二叉树的特点:
若完全二叉树的深度为k,则所有的叶子结点都出现在第k层或k-1层。对于任一结点,如果其右子树的最大层次为l,则其左子树的最大层次为l或l+1。
性质4:n个结点的完全二叉树深度为:[log2n]+1
性质5:若对一棵有n个结点的完全二叉树的结点按层序自左至右进行编号,则对于编号为i(1≦i≦n)的结点:
⑴ 若i=1:则结点i是二叉树的根,无双亲结点;否则,若i>1,则其双亲结点编号是[ i/2 ]。
⑵ 如果2i>n:则结点i为叶子结点,无左孩子;否则,其左孩子结点编号是2i。
⑶ 如果2i+1>n:则结点i无右孩子;否则,其右孩子结点编号是2i+1。
五,二叉树的存储
1,顺序存储结构
用 一组地址连续的存储单元依次“自上而下、自左至右”存储完全二叉树的数据元素。
对于完全二叉树上编号为i的结点元素存储在一维数组的下标值为i-1的分量中,
对于一般的二叉树,将其每个结点与完全二叉树上的结点相对照,存储在一维数组中。
2,链式存储结构
① 二叉链表结点。有三个域:一个数据域,两个分别指向左右子结点的指针域。
typedef struct BTNode
{
ElemType data ;
structBTNode *Lchild, *Rchild;
}BTNode;三叉链表结点。除二叉链表的三个域外,再增加一个指针域,用来指向结点的父结点,如图6-7(b)所示。
typedef struct BTNode_3
{
ElemType data ;
structBTNode_3 *Lchild, *Rchild, *parent ;
}BTNode_3;
六,二叉树的遍历
遍历二叉树(TraversingBinary Tree):是指按指定的规律对二叉树中的每个结点访问一次且仅访问一次。
若以L、D、R分别表示遍历左子树、遍历根结点和遍历右子树,则有六种遍历方案:DLR、LDR、LRD、DRL、RDL、RLD。若规定先左后右,则只有前三种情况三种情况,分别是:
DLR——先(根)序遍历。
LDR——中(根)序遍历。
LRD——后(根)序遍历。
1,先序遍历
递归算法:
若二叉树为空,则遍历结束;否则
⑴访问根结点;
⑵先序遍历左子树(递归调用本算法);
⑶先序遍历右子树(递归调用本算法)。
void PreorderTraverse(BTNode *T)
{
if (T!=NULL)
{
visit(T->data) ; /* 访问根结点 */
PreorderTraverse(T->Lchild) ;
PreorderTraverse(T->Rchild) ;
}
}
非递归算法:
设T是指向二叉树根结点的指针变量,非递归算法是:
若二叉树为空,则返回;否则,令p=T;
⑴ 访问p所指向的结点;
⑵ q=p->Rchild,若q不为空,则q进栈;
⑶ p=p->Lchild,若p不为空,转(1),否则转(4);
⑷ 退栈到p ,转(1),直到栈空为止。
#define MAX_NODE 50
void PreorderTraverse( BTNode *T)
{
BTNode *Stack[MAX_NODE] ,*p=T, *q ;
int top=0 ;
if (T==NULL) printf(" Binary Tree is Empty!\n") ;
else
{
do
{
visit( p-> data ) ;
q=p->Rchild ;
if ( q!=NULL ) stack[++top]=q ;
p=p->Lchild ;
if (p==NULL)
{
p=stack[top] ;
top-- ;
}
}while (p!=NULL) ;
}
}
同理,
2,中序遍历:
递归算法:
void InorderTraverse(BTNode *T)
{
if (T!=NULL)
{
InorderTraverse(T->Lchild) ;
visit(T->data) ; /* 访问根结点 */
InorderTraverse(T->Rchild) ;
}
}
非递归算法:
#define MAX_NODE 50
void InorderTraverse( BTNode *T)
{
BTNode *Stack[MAX_NODE] ,*p=T ;
int top=0 , bool=1 ;
if (T==NULL) printf("Binary Tree is Empty!\n") ;
else
{
do
{
while (p!=NULL)
{
stack[++top]=p ;
p=p->Lchild ;
}
if (top==0) bool=0 ;
else
{
p=stack[top] ;
top-- ;
visit( p->data ) ;
p=p->Rchild ;
}
}while (bool!=0) ;
}
}
3,后序遍历
递归算法:
void PostorderTraverse(BTNode *T)
{
if (T!=NULL)
{
PostorderTraverse(T->Lchild) ;
PostorderTraverse(T->Rchild) ;
visit(T->data) ; /* 访问根结点 */
}
}
非递归算法:
#define MAX_NODE 50
void PostorderTraverse( BTNode *T)
{
BTNode *S1[MAX_NODE] ,*p=T ;
int S2[MAX_NODE] , top=0 , bool=1 ;
if (T==NULL) printf("Binary Tree is Empty!\n") ;
else
{
do
{
while (p!=NULL)
{
S1[++top]=p ;
S2[top]=0 ;
p=p->Lchild ;
}
if (top==0) bool=0 ;
else if (S2[top]==0)
{
p=S1[top]->Rchild ;
S2[top]=1 ;
}
else
{
p=S1[top] ;
top-- ;
visit( p->data ) ;
p=NULL ;
/* 使循环继续进行而不至于死循环 */
}
}while (bool!=0) ;
}
}
4,层次遍历
层 次遍历二叉树,是从根结点开始遍历,按层次次序“自上而下,从左至右”访问树中的各结点。
设T是指向根结点的指针变量,层次遍历非递归算法是:
若二叉树为空,则返回;否则,令p=T,p入队;
⑴队首元素出队到p;
⑵访问p所指向的结点;
⑶将p所指向的结点的左、右子结点依次入队。直到队空为止。
#define MAX_NODE 50
void LevelorderTraverse( BTNode *T)
{
BTNode *Queue[MAX_NODE] ,*p=T ;
int front=0 , rear=0 ;
if (p!=NULL)
{
Queue[++rear]=p; /* 根结点入队 */
while (front<rear)
{
p=Queue[++front];
visit( p->data );
if (p->Lchild!=NULL)
Queue[++rear]=p->Lchild; /* 左结点入队 */
if (p->Rchild!=NULL)
Queue[++rear]=p->Rchild; /* 左结点入队 */
}
}
}