赞
踩
二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个结点。
如下图,就是一个二叉树:
①结点:包含一个数据元素及若干指向子树分支的信息。
②结点的度:一个结点拥有子树的数目称为节点的度。
③叶子结点:也称为终端结点,没有子树的结点或者度为零的结点。
④分支节点:也称为非终端结点,度不为零的结点称为非终端结点。
⑤树的度:树中所有结点的度的最大值。
⑥结点的层次:从根结点开始,假设根结点为第1层,根结点的子节点为第2层,依此类推,如果某一个结点位于第L层,则其子结点位于第L+1层。
⑦树的深度:也称为树的高度,树中所有结点的层次最大值称为树的深度。
⑧有序树:如果树中各棵子树的次序是有先后次序,则称该树为有序树。
⑨无序树:如果树中各棵子树的次序没有先后次序,则称该树为无序树。
⑩森林:由m(m≥0)棵互不相交的树构成一片森林。如果把一棵非空的树的根结点删除,则该树就变成了一片森林,森林中的树由原来根结点的各棵子树构成。
-------选自百度百科
注意区分树的深度和度。
二叉树特点:
二叉树五种基本形态:
在二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层,这样的二叉树称为满二叉树。
如下图:
定义:一棵深度为k的有n个结点的二叉树 ,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。
简单理解你可以给树结点从上到下,从左到右编号这些编号是连续的,如下图:
这就是一颗完全二叉树。
不完全的二叉树如下图:
序号为5的结点处不存在,但是之后有结点,序号不连续。
性质1:二叉树的第i层上至多有2i-1(i≥1)个结点。
性质2:深度为h的二叉树中至多含有2h-1个结点。
性质3:若在任意一棵二叉树中,有n0个叶子结点,有n2个度为2的节点,则必有n0=n2+1。
性质4:具有n个节点的满二叉树深为log2(n+1)。
性质5:若对一棵有n个结点的完全二叉树进行顺序编号(1≤i≤n),那么,对于编号为i(i≥1)的节点:
当i=1时,该结点为根,它无双亲结点。
当i>1时,该结点的双亲结点的编号为i/2。
若2i≤n,则有编号为2i的左结点,否则没有左结点。
若2i+1≤n,则有编号为2i+1的右结点,否则没有右结点。
二叉树每个结点包含两个孩子,所以为一个结点设计两个指针域和一个数据域,两个指针域分别指向两个孩子结点。
代码如下:
//树结点结构
typedef struct BiTNode{
TElemType data; //定义数据
struct BiTNode *lchild,*rchild; //左右子树
}BiTNode,*BiTree;
这里只展示出几种主要操作,一些附带的次要操作,会在完整代码中给出。
操作函数 | 操作结果 |
---|---|
InitBiTree(BiTree *T) | 初始化二叉树 |
DestroyBiTree(BiTree *T) | 销毁二叉树 |
CreateBiTree(BiTree *T) | 构造树 |
BiTreeEmpty(BiTree T) | 判断树是否为空 |
BiTreeDepth(BiTree T) | 返回树的深度 |
PreOrderTraverse(BiTree T) | 前序遍历 |
InOrderTraverse(BiTree T) | 中序遍历 |
PostOrderTraverse(BiTree T) | 后序遍历 |
代码如下:
/*用于构造二叉树*/ int treeIndex=1; typedef char String[24]; //0号单元存放长度 String str; //构造树,按照前序遍历,#表示空树 void CreateBiTree(BiTree *T){ TElemType ch; ch=str[treeIndex++]; if(ch=='#'){ *T=NULL; }else{ *T=(BiTree)malloc(sizeof(BiTNode)); if(!*T) exit(OVERFLOW); (*T)->data=ch; //生成根结点 CreateBiTree(&(*T)->lchild); //构造左子树 CreateBiTree(&(*T)->rchild); //构造右子树 } }
在这段代码中,使用了一个变量treeIndex
来记录当前读取到的字符串中的位置,从而实现对字符串的逐个读取。同时,也定义了一个类型别名String,表示字符串类型,其中0号单元存放长度。
在函数CreateBiTree中,首先读取当前位置的字符ch,如果为’#',则表示当前子树为空,直接将指针T指向空值NULL;否则,为当前结点分配内存空间,并将字符ch存储在结点的data中。接着,递归调用CreateBiTree函数,将左子树和右子树构造出来,并分别赋值给当前节点的左右子结点指针lchild
和rchild
。
需要注意的是,在这段代码中,使用了一个二级指针BiTree *T来表示当前结点指针的地址,而不是直接使用指针BiTree T。这是因为在递归过程中,需要对结点指针进行修改,而直接传递指针本身无法修改指针指向的地址,因此需要传递指针的地址,以便修改结点指针的值。
代码如下:
//销毁二叉树
void DestroyBiTree(BiTree *T){
if(*T){
if((*T)->lchild) //有左孩子
DestroyBiTree(&(*T)->lchild); //销毁左孩子的子树
if((*T)->rchild) //有右孩子
DestroyBiTree(&(*T)->rchild); //销毁右孩子子树
free(*T); //释放根结点
*T=NULL;
}
}
这部分光靠文字说明是很难理解的,将采用图解的形式进行解释。遍历这部分总体采用的是递归的思路,如果对递归不太熟悉,建议先去查看递归函数的有关内容,这边不会对递归函数进行介绍。
前序遍历从根结点开始,先遍历左子树,再遍历右子树。
图解:
代码如下:
//前序遍历
void PreOrderTraverse(BiTree T){
if(T==NULL)
return;
printf("%c",T->data);
PreOrderTraverse(T->lchild); //先序遍历左子树
PreOrderTraverse(T->rchild); //最后先序遍历右子树
}
中序遍历和前序遍历只是代码顺序上的区别,变成了先遍历左子树,然后打印结点,最后遍历右子树。
图解:
代码如下:
//中序遍历
void InOrderTraverse(BiTree T){
if(T==NULL)
return;
InOrderTraverse(T->lchild);
printf("%c",T->data);
InOrderTraverse(T->rchild);
}
后序遍历则是先遍历左子树,再遍历右子树,最后打印结点。
图解:
代码如下:
//后序遍历
void PostOrderTraverse(BiTree T){
if(T==NULL)
return;
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
printf("%c",T->data);
}
设置两个计数器i,j,一个记录左边子树最大层,另一个记录右边,最终返回最大的那个值,就是树的深度。
代码如下:
//返回树的深度 int BiTreeDepth(BiTree T){ int i,j; if(!T) return 0; if(T->lchild) i=BiTreeDepth(T->lchild); else i=0; if(T->rchild) j=BiTreeDepth(T->rchild); else j=0; return i>j?i+1:j+1; }
int main(){ int i; BiTree T; TElemType e1; InitBiTree(&T); StrAssign(str,"ABDH###E##CFI###G#j##"); CreateBiTree(&T); printf("构造空二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T)); e1=Root(T); printf("二叉树的根为: %c\n",e1); printf("\n前序遍历二叉树:"); PreOrderTraverse(T); printf("\n中序遍历二叉树:"); InOrderTraverse(T); printf("\n后序遍历二叉树:"); PostOrderTraverse(T); ClearBiTree(&T); printf("\n清除二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T)); i=Root(T); if(!i) printf("树空,无根\n"); system("pause"); return 0; }
构造空二叉树后,树空否?0(1:是 0:否) 树的深度=4
二叉树的根为: A
前序遍历二叉树:ABDHECFIGj
中序遍历二叉树:HDBEAIFCGj
后序遍历二叉树:HDEBIFjGCA
清除二叉树后,树空否?1(1:是 0:否) 树的深度=0
请按任意键继续. . .
下面附上完整代码:
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<math.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 100 typedef int Status; /*用于构造二叉树*/ int treeIndex=1; typedef char String[24]; //0号单元存放长度 String str; Status StrAssign(String T,char *chars){ int i; if(strlen(chars)>MAXSIZE) return ERROR; else { T[0]=strlen(chars); for(i=1;i<=T[0];i++) T[i]=*(chars+i-1); return OK; } } typedef char TElemType; TElemType Nil=' '; /* 字符型以空格符为空 */ Status visit(TElemType e){ printf("%c ",e); return OK; } //树结点结构 typedef struct BiTNode{ TElemType data; //定义数据 struct BiTNode *lchild,*rchild; //左右子树 }BiTNode,*BiTree; //构造空二叉树T Status InitBiTree(BiTree *T){ *T=NULL; return OK; } //销毁二叉树 void DestroyBiTree(BiTree *T){ if(*T){ if((*T)->lchild) //有左孩子 DestroyBiTree(&(*T)->lchild); //销毁左孩子的子树 if((*T)->rchild) //有右孩子 DestroyBiTree(&(*T)->rchild); //销毁右孩子子树 free(*T); //释放根结点 *T=NULL; } } #define ClearBiTree DestroyBiTree //构造树,按照前序遍历,#表示空树 void CreateBiTree(BiTree *T){ TElemType ch; ch=str[treeIndex++]; if(ch=='#'){ *T=NULL; }else{ *T=(BiTree)malloc(sizeof(BiTNode)); if(!*T) exit(OVERFLOW); (*T)->data=ch; //生成根结点 CreateBiTree(&(*T)->lchild); //构造左子树 CreateBiTree(&(*T)->rchild); //构造右子树 } } //判断树是否为空 Status BiTreeEmpty(BiTree T){ if(T) return FALSE; else return TRUE; } //返回树的深度 int BiTreeDepth(BiTree T){ int i,j; if(!T) return 0; if(T->lchild) i=BiTreeDepth(T->lchild); else i=0; if(T->rchild) j=BiTreeDepth(T->rchild); else j=0; return i>j?i+1:j+1; } //返回T的根 TElemType Root(BiTree T){ if(BiTreeEmpty(T)) return Nil; else return T->data; } //返回p结点返回值 TElemType Value(BiTree p){ return p->data; } //为结点赋值 void Assign(BiTree p,TElemType value){ p->data=value; } //前序遍历 void PreOrderTraverse(BiTree T){ if(T==NULL) return; printf("%c",T->data); PreOrderTraverse(T->lchild); //先序遍历左子树 PreOrderTraverse(T->rchild); //最后先序遍历右子树 } //中序遍历 void InOrderTraverse(BiTree T){ if(T==NULL) return; InOrderTraverse(T->lchild); printf("%c",T->data); InOrderTraverse(T->rchild); } //后序遍历 void PostOrderTraverse(BiTree T){ if(T==NULL) return; PostOrderTraverse(T->lchild); PostOrderTraverse(T->rchild); printf("%c",T->data); } int main(){ int i; BiTree T; TElemType e1; InitBiTree(&T); StrAssign(str,"ABDH###E##CFI###G#j##"); CreateBiTree(&T); printf("构造空二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T)); e1=Root(T); printf("二叉树的根为: %c\n",e1); printf("\n前序遍历二叉树:"); PreOrderTraverse(T); printf("\n中序遍历二叉树:"); InOrderTraverse(T); printf("\n后序遍历二叉树:"); PostOrderTraverse(T); ClearBiTree(&T); printf("\n清除二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T)); i=Root(T); if(!i) printf("树空,无根\n"); system("pause"); return 0; }
上述代码中有
"ABDH###E##CFI###G#j##"
,这个是用来构造二叉树用的,#代表空,那边不存在结点。注意需要表示出每个结点的左右孩子结点,若没有就用#代替
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。