树是一种数据结构,由n(n>=1)个有限结点组成的一个具有层次关系的集合。每颗树都有一个根结点,根结点下有若干个子结点,每一个非根结点有且只有一个父结点(根节点没有父结点)。除了根结点外,每个子结点又可以分为多个不相交的子树。
树的相关术语有:
树的结点(node):包含一个数据元素及若干指向子树的分支;
孩子结点(child node):结点的子树的根称为该结点的孩子;
双亲结点:B 结点是A 结点的孩子,则A结点是B 结点的双亲;
兄弟结点:同一双亲的孩子结点;
堂兄结点:同一层上结点;
祖先结点: 从根到该结点的所经分支上的所有结点子孙结点:以某结点为根的子树中任一结点都称为该结点的子孙
结点层:根结点的层定义为1;根的孩子为第二层结点,依此类推;
树的深度:树中最大的结点层
结点的度:结点子树的个数
树的度: 树中最大的结点度。
叶子结点:也叫终端结点,是度为 0 的结点;
分枝结点:度不为0的结点;
有序树:子树有序的树,如:家族树;
无序树:不考虑子树的顺序;
二叉树是一种特殊的树,每个结点最多有两个子树的树结构,即使某结点只有一个子树,也要区分左右子树,左子树和右子树是有顺序的。它有五种基本形态:空二叉树、根和空的左右子树、根和左子树、根和右子树、根和左右子树。
二叉树的性质表现为以下几点:
(1)深度为k的二叉树至多有2k-1个结点(k>=1)
(2)二叉树第i层上的结点数目最多为2i-1(i>=1)
(3)包含n个结点的二叉树的高度至少为(log2n)+1
(4)在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1。推论过程如下:
因为二叉树中所有结点的度数均不大于2,不妨设n0表示度为0的结点个数,n1表示度为1的结点个数,n2表示度为2的结点个数。三类结点加起来为总结点个数,于是便可得到:n=n0+n1+n2 (1)
由度之间的关系可得第二个等式:n=n00+n11+n2*2+1即n=n1+2n2+1 (2)
将(1)(2)组合在一起可得到n0=n2+1
1、满二叉树
先看下特殊的二叉树——满二叉树。满二叉树:除最后一层叶子节点(没有子节点的节点)外,每一层上的所有结点都有两个子结点。也就是说,如果一个二叉树的层数为K,且节点总数是(2^k) -1 ,比如图中层数为4,节点数为15,叶子节点为8、9~~15。在同样深度的二叉树中,满二叉树的节点个数最多,叶子树最多。
2、完全二叉树
完全二叉树是由满二叉树引出的一种效率很高的数据结构。对一棵具有n个节点的二叉树按层序排号,如果编号为i的节点与同样深度的满二叉树编号为i节点在二叉树中位置完全相同,就是完全二叉树。其中关键点是按层序编号,然后对应查找。满二叉树必须是完全二叉树,反过来不一定成立。
完全二叉树的特性:
1、同样结点数的二叉树,完全二叉树的高度最小
2、完全二叉树的叶子结点仅出现在最下边两层,并且最底层的叶子结点一定出现在左边,倒数第二层的叶子结点一定出现在右边。
3、完全二叉树中度为1的结点只有左孩子。
3、二叉树遍历
为什么研究二叉树的遍历?因为计算机只会处理线性序列,我们研究遍历,就是把树中的结点变成某种意义的线性序列,这给程序的实现带来了好处。
二叉树遍历指从树的根节点出发,按照某种次序依次访问二叉树中所有的结点,使得每个结点被访问仅且一次。从二叉树的递归定义可知,一棵非空的二叉树由根结点及左、右子树这三个基本部分组成,因此,在任一给定结点上,可以按某种次序执行三个操作:访问结点本身(N);遍历该结点的左子树(L);遍历该结点的右子树(R)。
以上三种操作有六种执行次序:NLR、LNR、LRN、NRL、RNL、RLN。前三种次序与后三种次序是对称的,此处讨论下前三种次序。
1、NLR为前序遍历(Preorder Traversal 亦称先序遍历)访问根结点的操作发生在遍历其左右子树之前。先访问根结点,再先序遍历左子树,最后再先序遍历右子树即根—左—右。
2、LNR为中序遍历(Inorder Traversal)。访问根结点的操作发生在遍历其左右子树之中(间)。先中序遍历左子树,然后再访问根结点,最后再中序遍历右子树即左—根—右。
3、LRN为后序遍历(Postorder Traversal)。访问根结点的操作发生在遍历其左右子树之后。先后序遍历左子树,然后再后序遍历右子树,最后再访问根结点即左—右—根。
由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。
以下附上源码程序,编译命令为gcc -o hello tree_exam.c。需要注意键盘输入一个结点时需要对应两个子结点,为空则用#表示。
#include<stdio.h>
#include<stdlib.h>
//二叉树的存储结构,一个数据域,2个指针域
typedef struct BiTNode
{
char data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
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);
}
void CreateBiTree(BiTree *T)
{
char ch;
scanf("%c",&ch);//1C#S##D##A#1## 一个结点对应两个子结点,为空则用#表示
if(ch=='#')
*T=NULL;
else
{
*T=(BiTree)malloc(sizeof(BiTNode));
if(!*T)
exit(-1);
(*T)->data=ch;//给节点的数据域赋值
printf("输入%c的左子树\n", ch);
CreateBiTree(&(*T)->lchild);//递归创建左子树
printf("输入%c的右子树\n", ch);
CreateBiTree(&(*T)->rchild);//递归创建右子树
}
}
void DestroyBiTree(BiTree T)
{
if(T)//如果T存在
{
DestroyBiTree(T->lchild);
DestroyBiTree(T->rchild);
free(T);
}
}
int main()
{
BiTree T;
printf("请输入二叉树的数据,并以#为空节点
");
CreateBiTree(&T);
printf("该树的先序遍历结果为:");
PreOrderTraverse (T);
printf("\n");
printf("该树的中序遍历结果为:");
InOrderTraverse(T);
printf("\n");
printf("该树的后序遍历结果为:");
PostOrderTraverse(T);
printf("\n");
DestroyBiTree(T);
return 0;
}
热门评论
org途虎???????
asd