树型结构是一类非常重要的非线性结构。直观地,树型结构是以分支关系定义的层次结构
树(tree)是由n(n≥0)个结点组成的有限集合T。n=0的树称为空树;对n>0的树,有:
(1)仅有一个特殊的结点称为根(root)结点,根结点没有前驱结点;
(2)当n>1时,除根结点外其余的结点分为m(m>0)个互不相交的有限集合T1,T2,…,Tm,其中每个集合Ti本身又是一棵树,称之为根的子树( subtree)。
注意:树的定义具有递归性,即“树中还有树”。
仅有一个根结点的树是最小树
树的表示形式
⑴ 倒悬树。是最常用的表示形式。
⑵ 嵌套集合(文氏图)。是一些集合的集体,对于任何两个集合,或者不相交,或者一个集合包含另一个集合。
⑶ 广义表形式。
⑷ 凹入法表示形式(凹入表)。
二叉树定义:是n(n≥0)个结点的有限集合,由一个根结点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成 。
基本特征:
① 每个结点最多只有两棵子树(不存在度大
于2的结点);
② 左子树和右子树次序不能颠倒(有序树)。
基本形态:
(1) 左右子树均非空的二叉树
(2) 只有左子树的二叉树
(3) 只有右子树的二叉树
(4) 只有根的二叉树
(5) 空二叉树
满二叉树的特点:
◆ 基本特点是每一层上的结点数总是最大结点数。
◆ 满二叉树的所有的支结点都有左、右子树。
◆ 可对满二叉树的结点进行连续编号,若规定从根结点开始,按“自上而下、自左至右”的原则进行。
完全二叉树(Complete Binary Tree):如果深度为k,由n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1到n的结点一一对应,该二叉树称为完全二叉树。
或深度为k的满二叉树中编号从1到n的前n个结点构成了一棵深度为k的完全二叉树。
其中 2k-1 ≦ n≦2k-1 。
完全二叉树是满二叉树的一部分,而满二叉树是完全二叉树的特例。
完全二叉树的特点:
若完全二叉树的深度为k ,则所有的叶子结点都出现在第k层或k-1层。对于任一结点,如果其右子树的最大层次为l,则其左子树的最大层次为l或l+1。
二叉树遍历:
顺着某一条搜索路径巡访二叉树中的结点,使
得每个结点均被访问一次,而且仅被访问一次。
先序遍历二叉树的操作定义:
若二叉树为空,则空操作;否则
(1) 访问根结点;
(2) 先序遍历左子树;
(3) 先序遍历右子树。
中序遍历二叉树的操作定义:
若二叉树为空,则空操作;否则
(1) 中序遍历左子树;
(2) 访问根结点;
(3) 中序遍历右子树。
后序遍历二叉树的操作定义:
若二叉树为空,则空操作;否则
(1) 后序遍历左子树;
(2) 后序遍历右子树;
(3) 访问根结点。
对于二叉树的遍历,分别讨论递归遍历算法和非递归遍历算法。递归遍历算法具有非常清晰的结构,但初学者往往难以接受或怀疑,不敢使用。实际上,递归算法是由系统通过使用堆栈来实现控制的。而非递归算法中的控制是由设计者定义和使用堆栈来实现的。
递归:
void PreorderTraverse(BTNode *T)
{ if (T!=NULL)
{ visit(T->data) ; /* 访问根结点 */
PreorderTraverse(T->Lchild) ;
PreorderTraverse(T->Rchild) ;
}
}
非递归:
#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) ;
}
}
平衡二叉树
结点的平衡因子(balancd factor用bf表示) :二叉树中某结点左子树的高度与右子树的高度之差称为该结点的平衡因子.
平衡二叉树是另一种形式的二叉查找树。其特点是:
左右子树深度之差的绝对值不大于1
称有这种特性的二叉树为平衡二叉树。
在算法中,可以通过平衡因子来具体实现平衡二叉树的定义。
从平衡因子的角度可以说,若一棵二叉树中所有结点的平衡因子的绝对值小于或等于1,则该树称为平衡二叉树。
右旋:
lc=p->lchild; /*lc指向B
p->lchild=lc->rchild; /*把B结点的右子树挂接为A的左子树
lc->rchild=p; /*A结点成为B的右孩子
p=lc; /*p指向新的根结点
左旋:
lc=p->rchild; /*lc指向B*/
p->rchild=lc->lchild; /*把B结点的左子树挂接为A的右子树*/
lc->lchild=p; /*A结点成为B的左孩子*/
p=lc; /*p指向新的根结点*/
左右旋:
p->lchild=c->rchild; /*把C的右子树挂接成A的左子树*/
b->rchild=c->lchild; /*把C的左子树挂接成B的右子树*/
c-lchild=b; /*把B挂接成C的左子树*/
c->rchild=p; /*把A挂接成C的右子树*/
右左旋:
p->rchild=c->lchild; /*把C的左子树挂接成A的右子树*/
b->lchild=c->rchild; /*把C的右子树挂接成B的左子树*/
c-rchild=b; /*把B挂接成C的右子树*/
c->lchild=p; /*把A挂接成C的左子树*/