目录
- 一、二叉树的遍历
- (一)二叉树的先序遍历(DLR)
- (二)二叉树的中序遍历(LDR)
- (三)二叉树的后序遍历(LRD)
- (四)先序、中序和后序遍历的相关性质
- (五)二叉树的层次遍历
- 二、二叉树的实现代码(链式存储)
- (一)二叉树的定义
- (二)二叉树的建立
- (三)广义表输出二叉树
- (四)二叉树的先、中、后遍历
- (五)二叉树的层次遍历
- (六)二叉树的深度
- (七)二叉树的叶子结点数
- (八)二叉树的结点总数
- ❤️循环队列的完整代码
一、二叉树的遍历
二叉树的遍历是按某种规定的顺序来对访问树中的所有结点,且每个结点仅被访问一次,由于二叉树由根结点(D)、左子树(L)和右子树(R)组成,可分为二叉树的先、中、后遍历另外还有每一层的遍历,即层次遍历,如下表:
遍历 | 访问结点的顺序 |
---|---|
先序遍历 | 根 左右 |
中序遍历 | 左根 右 |
后序遍历 | 左右根 |
二叉树的先、中、后序遍历都可以通过递归算法实现,递归结束的条件是T==NULL,即当二叉树为空时,遍历结束。
(一)二叉树的先序遍历(DLR)
二叉树的先序遍历中,首先是根结点,遍历完根结点的左子树,然后再遍历完根结点的右子树,依次下去至所有结点都遍历到,例如下图二叉树:
其先序遍历序列为ABDCE。
(二)二叉树的中序遍历(LDR)
二叉树的中序遍历中,首先是遍历完根结点的左子树,然后是根结点,最后遍历完根结点的右子树,依次下去至所有结点都遍历到,通过中序遍历可以得到一个递增的有序序列。
例如,一个二叉树中,根据中序遍历可知,中序遍历序列的最后一个结点一定是从根结点开始沿着右子树走到最底的结点,若它为叶子结点,则先序遍历序列和中序遍历序列的最后一个结点都为该结点;若不为叶子结点,它还有一个左子结点,则该左子结点是前序遍历序列的最后一个结点。
中序遍历序列为:DBACE,由于中序遍历序列的最后一个结点一定是从根结点开始沿着右子树走到最底的结点,所以中序遍历序列的最后一个结点为E,由于该结点是叶子结点,所以先序遍历序列和中序遍历序列的最后一个结点都为该结点。
先序:ABDCE
中序:DBACE
(三)二叉树的后序遍历(LRD)
二叉树的后序遍历中,首先是遍历完根结点的左子树,然后遍历完根结点的右子树,最后是根结点,依次下去至所有结点都遍历到,也就是从二叉树的底层往上层依次遍历。
该二叉树的后序遍历序列为:DBECA。
(四)先序、中序和后序遍历的相关性质
- 在二叉树的前序遍历、中序遍历和后序遍历序列中,
所有叶子结点的先后顺序是相同的
。
例如,该二叉树的三种遍历分别为ABDCE、DBACE、DBECA,可以看出叶子结点D和E的先后顺序总是一样的,D结点永远都在E结点的前面。
- 若一棵二叉树为空树或只有根结点,则其
先序遍历序列和后序遍历序列相同
;若二叉树为单右支二叉树或孤立结点,则其先序遍历序列和中序遍历序列相同
。
例如,下面单右支二叉树,先序遍历序列和中序遍历序列相同的,都为ABC:
- 若一棵二叉树为空树或任一结点都缺右子树的单支树,则其
中序遍历序列和后序遍历序列相同
。
例如,下面二叉树,其中任一结点至多只有左子树,中序遍历序列和后序遍历序列相同,都为DCBA:
- 若一棵非空的二叉树只有一个叶子结点,或二叉树的高度等于结点个数,则其
先序遍历序列和后序遍历序列相反
。
例如,下面二叉树只有一个叶子结点,其结点个数为3也等于高度h=3,所以其先序遍历序列和后序遍历序列相反,即ABC和CBA:
(五)二叉树的层次遍历
层次遍历中,层次优先,当对一层的结点都遍历完后,遍历下一层,按照次序对每个结点的左、右孩子进行遍历。
例如,下面二叉树,其层次遍历为ABCDEFG:
层次遍历二叉树中可以通过链式队列实现,首先二叉树的根结点入队,然后进入循环,循环条件为队列是否为空,若不为空,则当前队头结点出队,此时该结点被访问到,并将该结点的左、右孩子结点插入到队列的队尾。
- 若一棵二叉树为空树或任一结点至多只有右子树,则
中序遍历序列与层次遍历序列相同
。
二、二叉树的实现代码(链式存储)
(一)二叉树的定义
通过链式存储结构实现代码如下,其中包含数据域和两个指针:
#incldue<stdio.h>
/*二叉树的定义*/
typedef struct BNode {int data; //数据域struct BNode *lchild,*rchild; //左孩子、右孩子指针
} *BTree;
(二)二叉树的建立
创建一个二叉树,按二叉树的顺序(二叉树带空指针的顺序,空指针也算进去),即根结点、左子树、右子树的顺序依次输入结点的值【使用的顺序是先序序列】,若其中有空结点,用0表示,其中使用到递归的方法建立左右孩子结点,实现代码如下:
#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,当对于左孩子结点或右孩子结点时,此时输出一个左括号“(
”,递归处理左子树,输出一个“,
”用于隔开结点,然后递归处理右子树,输出一个右括号“)”,从而完成一个根结点以下的两个左/右结点处理。
/*广义表输出二叉树*/
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(")"); //输出一个右括号}}}
}
例如,一个二叉树如下图,通过链式存储结构实现建立二叉树并输出。
代码如下:
#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、先序遍历二叉树:
/*先序遍历二叉树*/
bool ProTree(BTree T) {if(T==NULL)return false; //递归结束else {printf("%c ",T->data); //输出当前结点的数据域ProTree(T->lchild); //递归继续遍历该结点的左子树ProTree(T->rchild); //递归继续遍历该结点的右子树return true;}
}
运行结果如下:
2、中序遍历二叉树:
/*中序遍历二叉树*/
bool InTree(BTree T) {if(T==NULL)return false; //递归结束else {InTree(T->lchild); //递归继续遍历该结点的左子树printf("%c ",T->data); //输出当前结点的数据域InTree(T->rchild); //递归继续遍历该结点的右子树return true;}
}
运行结果如下:
3、后序遍历二叉树:
/*后序遍历二叉树*/
bool PostTree(BTree T) {if(T==NULL)return false; //递归结束else {PostTree(T->lchild); //递归继续遍历该结点的左子树PostTree(T->rchild); //递归继续遍历该结点的右子树printf("%c ",T->data); //输出当前结点的数据域return true;}
}
运行结果如下:
(五)二叉树的层次遍历
层次遍历二叉树:
/*7、层次遍历二叉树*/
void LevelTree(BTree T) {BTree q[MAXSIZE]; //MAXSIZE的值可自行定义int front=0,rear=0; //初始化队头指针和队尾指针为0if(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;}}
}
也是上图中的二叉树,进行层次遍历,运行结果如下:
(六)二叉树的深度
二叉树的深度也是求最大深度,也是采用递归思想,分别递归计算左、右子树的深度,然后从左、右子树的深度中返回最大值,即为二叉树的深度,实现代码如下:
/*二叉树的深度*/
int DepthTree(BTree T) {int ldepth=0,rdepth=0; //分别代表左、右子树的深度,初始值都为0if(T==NULL)return 0;else {ldepth=DepthTree(T->lchild); //递归继续统计结点的左子树深度rdepth=DepthTree(T->rchild); //递归继续统计结点的右子树深度if(ldepth>rdepth) //求最大深度return ldepth+1;elsereturn rdepth+1;}
}
对于上图中的二叉树,运行结果如下:
(七)二叉树的叶子结点数
求一个二叉树的叶子结点数,也是递归方法实现,我们知道若一个结点的左、右孩子都为空,则这说明这是一个叶子结点,通过递归,最后return返回叶子结点数,实现代码如下:
/*二叉树的叶子结点数*/
int LeavesNum(BTree T) {if(T!=NULL) { //当根结点不为空if(T->lchild==NULL&&T->rchild==NULL) //若一个结点的左、右孩子都为空,即这是一个叶子结点 return 1;}return (LeavesNum(T->lchild)+LeavesNum(T->rchild));
}
对于上图中的二叉树,运行结果如下:
(八)二叉树的结点总数
也是递归,当二叉树不为空时,二叉树的结点总数等于左子树结点个数+右子树结点个数,然后加1(二叉树的根结点),实现代码如下:
/*求二叉树的结点总数*/
int SumLeaves(BTree T) {if(T!=NULL)return (SumLeaves(T->lchild)+SumLeaves(T->rchild)+1);
}
对于上图中的二叉树,运行结果如下:
❤️循环队列的完整代码
循环队列的完整代码如下:
#incldue<stdio.h>
#include <malloc.h>
/*二叉树的定义*/
typedef struct BNode {int data; //数据域struct BNode *lchild,*rchild; //左孩子、右孩子指针
} *BTree;/*二叉树的建立*/
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;
}/*广义表输出二叉树*/
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(")"); //输出一个右括号}}}
}/*先序遍历二叉树*/
bool ProTree(BTree T) {if(T==NULL)return false; //递归结束else {printf("%c ",T->data); //输出当前结点的数据域ProTree(T->lchild); //递归继续遍历该结点的左子树ProTree(T->rchild); //递归继续遍历该结点的右子树return true;}
}/*中序遍历二叉树*/
bool InTree(BTree T) {if(T==NULL)return false; //递归结束else {InTree(T->lchild); //递归继续遍历该结点的左子树printf("%c ",T->data); //输出当前结点的数据域InTree(T->rchild); //递归继续遍历该结点的右子树return true;}
}/*后序遍历二叉树*/
bool PostTree(BTree T) {if(T==NULL)return false; //递归结束else {PostTree(T->lchild); //递归继续遍历该结点的左子树PostTree(T->rchild); //递归继续遍历该结点的右子树printf("%c ",T->data); //输出当前结点的数据域return true;}
}/*层次遍历二叉树*/
void LevelTree(BTree T) {BTree q[100]; //MAXSIZE的值可自行 定义int front=0,rear=0; //初始化队头指针和队尾指针为0if(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;}}
}/*二叉树的深度*/
int DepthTree(BTree T) {int ldepth=0,rdepth=0; //分别代表左、右子树的深度,初始值都为0if(T==NULL)return 0;else {ldepth=DepthTree(T->lchild); //递归继续统计结点的左子树深度rdepth=DepthTree(T->rchild); //递归继续统计结点的右子树深度if(ldepth>rdepth) //求最大深度return ldepth+1;elsereturn rdepth+1;}
}/*二叉树的叶子结点数*/
int LeavesNum(BTree T) {if(T!=NULL) { //当根结点不为空if(T->lchild==NULL&&T->rchild==NULL) //若一个结点的左、右孩子都为空,即这是一个叶子结点 return 1;}return (LeavesNum(T->lchild)+LeavesNum(T->rchild));
}/*求二叉树的结点总数*/
int SumLeaves(BTree T) {if(T!=NULL)return (SumLeaves(T->lchild)+SumLeaves(T->rchild)+1);
}