二叉树的总结
2014-02-19 17:05
459 查看
一,二叉树的定义
二叉树(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,先序遍历
递归算法:
若二叉树为空,则遍历结束;否则
⑴访问根结点;
⑵先序遍历左子树(递归调用本算法);
⑶先序遍历右子树(递归调用本算法)。
非递归算法:
设T是指向二叉树根结点的指针变量,非递归算法是:
若二叉树为空,则返回;否则,令p=T;
⑴ 访问p所指向的结点;
⑵ q=p->Rchild,若q不为空,则q进栈;
⑶ p=p->Lchild,若p不为空,转(1),否则转(4);
⑷ 退栈到p ,转(1),直到栈空为止。
同理,
2,中序遍历:
递归算法:
非递归算法:
3,后序遍历
递归算法:
非递归算法:
4,层次遍历
层 次遍历二叉树,是从根结点开始遍历,按层次次序“自上而下,从左至右”访问树中的各结点。
设T是指向根结点的指针变量,层次遍历非递归算法是:
若二叉树为空,则返回;否则,令p=T,p入队;
⑴队首元素出队到p;
⑵访问p所指向的结点;
⑶将p所指向的结点的左、右子结点依次入队。直到队空为止。
二叉树(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; /* 左结点入队 */ } } }
相关文章推荐
- “传奇”图象数据存储方式
- SQL Server误区30日谈 第18天 有关FileStream的存储,垃圾回收以及其它
- PHP 存储文本换行实现方法
- 注册表中存储数据库链接字符串的方法
- 深入PHP变量存储的详解
- 对C语言中递归算法的深入解析
- c#汉诺塔的递归算法与解析
- 关于PHP递归算法和应用方法介绍
- Android App中各种数据保存方式的使用实例总结
- C++二叉树结构的建立与基本操作
- 深入遍历二叉树的各种操作详解(非递归遍历)
- Java递归算法的使用分析
- python二叉树的实现实例
- 我对块存储,文件存储,对象存储的认识
- 深入浅出全解SATA存储解决方案
- netapp 相关常识
- VMware vSphere 4.1虚拟化学习手册7:存储基础
- VMware vSphere 4.1虚拟化学习手册8:配置iSCSI及NFS存储
- vSphere 5.0 存储特点一-VMFS-5
- 正确面对硬盘存储、数据恢复