目录
一、二叉树的遍历
二叉树的遍历是按某种规定的顺序来对访问树中的所有结点,且每个结点仅被访问一次,由于二叉树由根结点(D)、左子树(L)和右子树(R)组成,以下是二叉树的先、中、后以及层次遍历。
二叉树的先、中、后序遍历都可以通过递归算法实现,递归结束的条件是T==NULL,即当二叉树为空时,遍历结束。
(一)二叉树的先序遍历(DLR)
二叉树的先序遍历中,首先是根结点,遍历完根结点的左子树,然后再遍历完根结点的右子树,依次下去至所有结点都遍历到,例如上图二叉树,其先序遍历就是abefcgh
。
(二)二叉树的中序遍历(LDR)
二叉树的中序遍历中,首先是遍历完根结点的左子树,然后是根结点,最后遍历完根结点的右子树,依次下去至所有结点都遍历到,通过中序遍历可以得到一个递增的有序序列。
例如,一个二叉树中,根据中序遍历可知,中序遍历序列的最后一个结点一定是从根结点开始沿着右子树走到最底的结点,若它为叶子结点,则先序遍历序列和中序遍历序列的最后一个结点都为该结点【如上图二叉树】;若不为叶子结点,它还有一个左子结点,则该左子结点是前序遍历序列的最后一个结点。
它的先序遍历序列为:ABDECF,中序遍历序列为:DBEAFC,由于中序遍历序列的最后一个结点一定是从根结点开始沿着右子树走到最底的结点,所以中序遍历序列的最后一个结点为C,且结点C并不是叶子结点,它还有一个左子结点F,所以该结点F为先序遍历序列的最后一个结点。
(三)二叉树的后序遍历(LRD)
二叉树的后序遍历中,首先是遍历完根结点的左子树,然后遍历完根结点的右子树,最后是根结点,依次下去至所有结点都遍历到,也就是从二叉树的底层往上层依次遍历。
- 在二叉树的前序遍历、中序遍历和后序遍历序列中,
所有叶子结点的先后顺序是相同的
。 - 若一棵二叉树为空树或只有根结点,则其
先序遍历序列和后序遍历序列相同
;若二叉树为单右支二叉树或孤立结点,则其先序遍历序列和中序遍历序列相同
。 - 若一棵二叉树为空树或任一结点至多只有左子树,则其
中序遍历序列和后序遍历序列相同
。 - 若一棵非空的二叉树只有一个叶子结点,或二叉树的高度等于结点个数,则其
先序遍历序列和后序遍历序列相反
。
(四)二叉树的层次遍历
层次遍历中,层次优先,当对一层的结点都遍历完后,遍历下一层,按照次序对每个结点的左、右孩子进行遍历。
- 层次遍历二叉树中可以通过链式队列实现,首先二叉树的根结点入队,然后进入循环,循环条件为队列是否为空,若不为空,则当前队头结点出队,此时该结点被访问到,并将该结点的左、右孩子结点插入到队列的队尾。
- 若一棵二叉树为空树或任一结点至多只有右子树,则其
中序遍历序列与层次遍历序列相
同。
二、二叉树的实现代码(链式存储)
(一)二叉树的定义
通过链式存储结构实现代码如下,其中包含数据域和两个指针:
c
#incldue<stdio.h>
/*二叉树的定义*/
typedef struct BNode {
int data; //数据域
struct BNode *lchild,*rchild; //左孩子、右孩子指针
} *BTree;
(二)二叉树的建立
创建一个二叉树,按二叉树的顺序(二叉树带空指针的顺序,空指针也算进去),即根结点、左子树、右子树的顺序依次输入结点的值【使用的顺序是先序序列】,若其中有空结点,用0表示,其中使用到递归的方法建立左右孩子结点,实现代码如下:
c
#include <malloc.h>
/*二叉树的建立*/
BTree CreateTree() {
BTree T;
char ch;
scanf("%c",&ch);
getchar(); //getchar()用于接收每次输入字符结点后的回车符,从而以便输入下一个字符结点
if(ch=='0') //当为0时,将结点置空
T=NULL;
else {
T=(BTree)malloc(sizeof(BTree)); //分配一个新的结点
T->data=ch;
printf("请输入%c结点的左孩子结点:",T->data);
T->lchild=CreateTree(); //通过递归建立左孩子结点
printf("请输入%c结点的右孩子结点:",T->data);
T->rchild=CreateTree(); //通过递归建立右孩子结点
}
return T;
}
(三)广义表输出二叉树
通过广义表来显示建立的二叉树,一个非空的二叉树T,当对于左孩子结点或右孩子结点时,此时输出一个左括号"(
",递归处理左子树,输出一个",
"用于隔开结点,然后递归处理右子树,输出一个右括号")",从而完成一个根结点以下的两个左/右结点处理。
c
/*广义表输出二叉树*/
void ShowTree(BTree T) {
if(T!=NULL) {
//当二叉树不为空时
printf("%c",T->data); //输入出该结点的数据域
if(T->lchild!=NULL) { //若该结点的左子树不为空
printf("("); //输出一个左括号
ShowTree(T->lchild); //通过递归继续输出结点的左子树结点下的各结点
if(T->rchild!=NULL) { //若该结点右子树不为空
printf(","); //输出一个逗号
ShowTree(T->rchild); //通过递归继续输出结点的右子树结点下的各结点
}
printf(")"); //输出一个右括号
} else { //若左子树为空,右子树不为空
if(T->rchild!=NULL) {
printf("("); //输出一个左括号
ShowTree(T->lchild); //通过递归继续输出结点的左子树结点下的各结点
if(T->rchild!=NULL) { //若该结点的右子树不为空
printf(","); //输出一个逗号
ShowTree(T->rchild); //通过递归继续输出结点的右子树结点下的各结点
}
printf(")"); //输出一个右括号
}
}
}
}
例如,一个二叉树如下图,通过链式存储结构实现建立二叉树并输出。
代码如下:
c
#include <stdio.h>
#include <malloc.h>
/*1、二叉树的定义*/
typedef struct BNode {
int data; //数据域
struct BNode *lchild,*rchild; //左孩子、右孩子指针
} *BTree;
/*2、二叉树的建立*/
BTree CreateTree() {
BTree T;
char ch;
scanf("%c",&ch);
getchar(); //getchar()用于接收每次输入字符结点后的回车符,从而以便输入下一个字符结点
if(ch=='0') //当为0时,将结点置空
T=NULL;
else {
T=(BTree)malloc(sizeof(BTree)); //分配一个新的结点
T->data=ch;
printf("请输入%c结点的左孩子结点:",T->data);
T->lchild=CreateTree(); //通过递归建立左孩子结点
printf("请输入%c结点的右孩子结点:",T->data);
T->rchild=CreateTree(); //通过递归建立右孩子结点
}
return T;
}
/*3、广义表输出二叉树*/
void ShowTree(BTree T) {
if(T!=NULL) {
//当二叉树不为空时
printf("%c",T->data); //输入出该结点的数据域
if(T->lchild!=NULL) { //若该结点的左子树不为空
printf("("); //输出一个左括号
ShowTree(T->lchild); //通过递归继续输出结点的左子树结点下的各结点
if(T->rchild!=NULL) { //若该结点右子树不为空
printf(","); //输出一个逗号
ShowTree(T->rchild); //通过递归继续输出结点的右子树结点下的各结点
}
printf(")"); //输出一个右括号
} else { //若左子树为空,右子树不为空
if(T->rchild!=NULL) {
printf("("); //输出一个左括号
ShowTree(T->lchild); //通过递归继续输出结点的左子树结点下的各结点
if(T->rchild!=NULL) { //若该结点的右子树不为空
printf(","); //输出一个逗号
ShowTree(T->rchild); //通过递归继续输出结点的右子树结点下的各结点
}
printf(")"); //输出一个右括号
}
}
}
}
/*主函数*/
int main() {
BTree T;
T=NULL;
printf("请输入二叉树的根结点:");
T=CreateTree(); //建立二叉树
printf("建立的二叉树如下:\n");
ShowTree(T); //通过广义表显示二叉树
}
依次输入各个结点的左右孩子结点,若结点不存在则输入0,例如树中结点d的左孩子结点不存在,结点f、g、h、i、j的左右孩子都不存在。
运行结果如下,结果通过广义表的定义显示:
(四)二叉树的先、中、后遍历
例如对下图这个二叉树,进行先、中、后遍历:
1、先序遍历二叉树:
c
/*先序遍历二叉树*/
bool ProTree(BTree T) {
if(T==NULL)
return false; //递归结束
else {
printf("%c ",T->data); //输出当前结点的数据域
ProTree(T->lchild); //递归继续遍历该结点的左子树
ProTree(T->rchild); //递归继续遍历该结点的右子树
return true;
}
}
运行结果如下:
2、中序遍历二叉树:
c
/*中序遍历二叉树*/
bool InTree(BTree T) {
if(T==NULL)
return false; //递归结束
else {
InTree(T->lchild); //递归继续遍历该结点的左子树
printf("%c ",T->data); //输出当前结点的数据域
InTree(T->rchild); //递归继续遍历该结点的右子树
return true;
}
}
运行结果如下:
3、后序遍历二叉树:
c
/*后序遍历二叉树*/
bool PostTree(BTree T) {
if(T==NULL)
return false; //递归结束
else {
PostTree(T->lchild); //递归继续遍历该结点的左子树
PostTree(T->rchild); //递归继续遍历该结点的右子树
printf("%c ",T->data); //输出当前结点的数据域
return true;
}
}
运行结果如下:
(五)二叉树的层次遍历
层次遍历二叉树:
c
/*7、层次遍历二叉树*/
void LevelTree(BTree T) {
BTree q[MAXSIZE]; //MAXSIZE的值可自行定义
int front=0,rear=0; //初始化队头指针和队尾指针为0
if(T!=NULL) { //当二叉树不为空
q[rear++]=T; //根结点入队
while(front!=rear) { //当队列不为空时
BTree head=q[front++];
printf("%c ",head->data); //访问队头结点的数据域
if(head->lchild) //若当前结点的左孩子存在,将队头结点的左孩子入队
q[rear++]=head->lchild;
if(head->rchild) //若当前结点的右孩子存在,将队头结点的右孩子入队
q[rear++]=head->rchild;
}
}
}
也是上图中的二叉树,进行层次遍历,运行结果如下:
(六)二叉树的深度
二叉树的深度也是求最大深度,也是采用递归思想,分别递归计算左、右子树的深度,然后从左、右子树的深度中返回最大值,即为二叉树的深度,实现代码如下:
c
/*二叉树的深度*/
int DepthTree(BTree T) {
int ldepth=0,rdepth=0; //分别代表左、右子树的深度,初始值都为0
if(T==NULL)
return 0;
else {
ldepth=DepthTree(T->lchild); //递归继续统计结点的左子树深度
rdepth=DepthTree(T->rchild); //递归继续统计结点的右子树深度
if(ldepth>rdepth) //求最大深度
return ldepth+1;
else
return rdepth+1;
}
}
对于上图中的二叉树,运行结果如下:
(七)二叉树的叶子结点数
求一个二叉树的叶子结点数,也是递归方法实现,我们知道若一个结点的左、右孩子都为空,则这说明这是一个叶子结点,通过递归,最后return返回叶子结点数,实现代码如下:
c
/*二叉树的叶子结点数*/
int LeavesNum(BTree T) {
if(T!=NULL) { //当根结点不为空
if(T->lchild==NULL&&T->rchild==NULL) //若一个结点的左、右孩子都为空,即这是一个叶子结点
return 1;
}
return (LeavesNum(T->lchild)+LeavesNum(T->rchild));
}
对于上图中的二叉树,运行结果如下:
(八)二叉树的结点总数
也是递归,当二叉树不为空时,二叉树的结点总数等于左子树结点个数+右子树结点个数,然后加1(二叉树的根结点),实现代码如下:
c
/*求二叉树的结点总数*/
int SumLeaves(BTree T) {
if(T!=NULL)
return (SumLeaves(T->lchild)+SumLeaves(T->rchild)+1);
}
对于上图中的二叉树,运行结果如下: