二叉树是《数据结构》中的一种较重要的数据类型,关于它的基本操作有:二叉树的创建、二叉树的遍历、求二叉树的高度等。
本文是对近期学习二叉树的一点总结,涉及的主要操作如下:
- 二叉树的创建:基于递归的先序创建、层序创建
- 二叉树的递归遍历:先序遍历、中序遍历、后序遍历
- 二叉树的非递归遍历:先序遍历、中序遍历、后续遍历、层序遍历
- 输出二叉树的叶子结点
- 求二叉树的高度
二叉树有数组实现和链表实现两种形式,但数组实现不如链表灵活,本文涉及的二叉树的各种操作都是基于链表。
定义二叉树结点类型:
typedef struct BinNode
{
ElementType data;
BinNode *Left;
BinNode *Right;
}BinTree;
一、二叉树的创建
二叉树是非线性的结构,创建一棵二叉树必须首先确定树中结点的输入顺序,常有的方法是先序创建和层序创建。
(一)先序创建
先序创建一棵二叉树,其操作步骤主要是基于二叉树的先序遍历。对于整棵树中的任意一棵子树,先输入其父结点,再输入其左孩子结点、右孩子结点。
像如下一棵二叉树,若以输入‘0’表示结点为空,按先序创建输入的结点顺序为:A B C 0 0 D E 0 G F 0 0 0
按先序遍历创建二叉树的代码如下:
/*按先序遍历创建二叉树*/
BinTree *CreatePre(BinTree* &BT)
{
char ch;
cout<<"ch=";
cin>>ch;
if(ch=='0')
BT=NULL;
else{
BT=new BinTree;
BT->data=ch;
BT->Left=CreatePre(BT->Left);
BT->Right=CreatePre(BT->Right);
}
return(BT);
}
(二)层序创建
层序创建所用的结点输入序列是按树的从上至下从左至右的顺序形成的,如上图的二叉树按层序创建,各结点的输入顺序为:A B 0 C D 0 0 E F 0 G 0 0
在构造二叉树的过程中,需要一个队列暂时存储各结点地址。过程如下:
(1)输入第一个数据:
若为0,表示此树为空,将空指针赋给根指针,树构造完毕;
若不为空,动态分配一个结点单元,并存入输入的数据,同时将该结点放入队列。
(2)若队列不为空,从队列中取出一个结点地址,并建立该结点的左右孩子:
从输入序列中读入下一数据:若读入的数据为0,将出队结点的左孩子指针置空;否则,分配一个结点单元,存入所读数值,并将其置为出队结点的左孩子,同时将此孩子地址入队。
再从输入序列中读入下一个数据:若读入的数据为0,将出队结点的右孩子指针置空;否则,分配一个结点单元,存入所读数值,并将其置为出队结点的右孩子,同时将此孩子地址入队。
(3)重复(2),直到队空再无结点入队。
因这一过程涉及有关队列的操作,现将关于队列的操作写在下面:
1.定义队列结点类型:
/*定义队列结点类型*/
struct QueNode
{
BinTree *node;
QueNode *next;
};
/*定义队列结点的指针*/
struct LinkQueue
{
QueNode *Front;
QueNode *Rear;
};
2.初始化队列
/*初始化队列*/
void CreateQueue(LinkQueue *Q)
{
QueNode *Node;
Node=new QueNode;
Node->next=NULL;
Q->Front=Node;
Q->Rear=Node;
}
3.判断队列是否为空
/*判断队列是否为空*/
bool IsEmpty(LinkQueue *Q)
{
return(Q->Front->next==NULL && Q->Rear->next==NULL);
}
4.将二叉树结点插入队尾
/*将二叉树结点插入队中*/
void InsertQueue(LinkQueue *Q,BinTree *X)
{
QueNode *P;
P=new QueNode;
P->node=X;
P->next=NULL;
if(IsEmpty(Q))
Q->Front->next=Q->Rear=P;
else{
Q->Rear->next=P;
Q->Rear=P;
}
}
5.返回并删除队头结点
/*返回并删除队头结点*/
BinTree *DeleteQueue(LinkQueue *Q)
{
QueNode *P;
BinTree *X;
if(IsEmpty(Q)){
cout<<"队空"<<endl;
return NULL;
}
else{
P=Q->Front->next;
X=P->node;
Q->Front->next=P->next;
if(Q->Rear==P)
Q->Rear=Q->Front;
delete P;
return(X);
}
}
层序创建二叉树的代码如下:
/*层序创建二叉树*/
void CreateLev(BinTree* &BT)
{
BinTree *T;
LinkQueue *Q;
Q=new LinkQueue;
CreateQueue(Q);
char ch;
cout<<"ch=";
cin>>ch;
if(ch!='0'){
BT=new BinTree;
BT->data=ch;
BT->Left=NULL;
BT->Right=NULL;
InsertQueue(Q,BT);
}
else
return;
while(!IsEmpty(Q)){
T=DeleteQueue(Q);
cout<<"ch=";
cin>>ch;
if(ch!='0'){
T->Left=new BinTree;
T->Left->data=ch;
T->Left->Left=T->Left->Right=NULL;
InsertQueue(Q,T->Left);
}
else
T->Left=NULL;
cout<<"ch=";
cin>>ch;
if(ch!='0'){
T->Right=new BinTree;
T->Right->data=ch;
T->Right->Left=T->Right->Right=NULL;
InsertQueue(Q,T->Right);
}
else
T->Right=NULL;
}
}
二、二叉树的递归遍历
(一)先序遍历
对结点的访问在其左右子树遍历之前,遍历从根结点开始,具体过程:
访问根结点;
先序遍历其左子树;
先序遍历其右子树。
递归实现的代码如下:
/*先序遍历*/
void PreorderTraversal(BinTree* &BT)
{
if(BT){
cout<<BT->data<<' ';
PreorderTraversal(BT->Left);
PreorderTraversal(BT->Right);
}
}
(二)中序遍历
遍历过程是:
中序遍历左子树;
访问根结点;
中序遍历右子树。
递归实现的代码如下:
/*中序遍历*/
void InorderTraversal(BinTree* &BT)
{
if(BT){
InorderTraversal(BT->Left);
cout<<BT->data;
InorderTraversal(BT->Right);
}
}
(三)后序遍历
遍历过程是:
中序遍历左子树;
中序遍历右子树;
访问根结点。
递归实现的代码如下:
/*后序遍历*/
void PostorderTraversal(BinTree* &BT)
{
if(BT){
PostorderTraversal(BT->Left);
PostorderTraversal(BT->Right);
cout<<BT->data;
}
}
三、二叉树的非递归遍历
二叉树的非递归遍历需要先从根结点开始,沿二叉树的左子树或右子树深入,当深入不下去时,再返回根结点。在这一过程中,返回结点的顺序与进入结点的顺序相反,即先入后出,可用堆栈来辅助实现。
有关链栈的操作:
1.定义链栈结构
/*定义链栈结构*/
typedef struct SNode
{
BinTree *node;
SNode *next;
}LinkStack;
2.初始化链栈
/*初始化链栈*/
LinkStack *CreateStack()
{
LinkStack *S;
S=new LinkStack;
S->next=NULL;
return(S);
}
3.入栈:将二叉树结点插入栈中
/*入栈:将结点插入栈顶*/
void Push(LinkStack *S,BinTree *X)
{
LinkStack *P;
P=new LinkStack;
P->node=X;
P->next=S->next;
S->next=P;
}
4.出栈:从栈顶弹出二叉树结点
/*出栈:从栈顶弹出二叉树结点*/
BinTree *Pop(LinkStack *S)
{
LinkStack *P;
BinTree *T;
if(S->next==NULL)
T=NULL;
else{
P=S->next;
T=P->node;
S->next=P->next;
delete P;
}
return(T);
}
(一)先序遍历
遍历过程:
遇到一个结点,访问之,将它压栈;
遍历它的左子树;
当左子树遍历结束后,从栈顶弹出这个结点,再先序遍历它的右子树。
/*非递归先序遍历*/
void NoRecuPreorder(BinTree* &BT)
{
BinTree *T;
LinkStack *S;
S=CreateStack();
T=BT;
while(T || S->next!=NULL){
while(T){
cout<<T->data;
Push(S,T);
T=T->Left;
}
T=Pop(S);
T=T->Right;
}
}
(二)中序遍历
遍历过程:
遇到一个结点,将它压栈,并遍历它的左子树;
当左子树遍历结束后,从栈顶弹出这个结点并访问它;
按其右指针再中序遍历该结点的右子树。
/*非递归中序遍历*/
void NoRecuInorder(BinTree* &BT)
{
BinTree *T;
LinkStack *S;
S=CreateStack();
T=BT;
while(T || S->next!=NULL){
while(T){
Push(S,T);
T=T->Left;
}
T=Pop(S);
cout<<T->data;
T=T->Right;
}
}
(三)后序遍历
非递归后序遍历,对每个结点,需要两次将其入栈。具体的遍历过程如下:
遇到一个结点,将它压栈,并遍历它的左子树;
当沿左子树无法深入,弹出该结点获得其右指针信息,再将其压入栈中,遍历其右子树;
当右子树无法深入,弹出结点并访问它。
由于需要将一个结点两次入栈、弹出(第一次入栈后遍历其左子树;第一次弹出以获得其指向右子树的指针信息;第二次入栈后遍历其右子树;第二次弹出后访问该结点),因此可在二叉树的结点定义中增加一个bool(或int)型的Tag变量,当变量值为true(1),表示遍历或已遍历其左子树;当变量值为false(0),表示遍历或已遍历其右子树。
新增Tag变量后的二叉树结点类型:
/*定义二叉树结点类型*/
typedef struct BinNode
{
ElementType data;
BinNode *Left;
BinNode *Right;
bool Tag;
}BinTree;
后序遍历函数代码:
/*非递归后序遍历*/
void NoRecuPostorder(BinTree* &BT)
{
BinTree *T;
LinkStack *S;
S=CreateStack();
T=BT;
T->Tag=true;
while(T || S->next!=NULL){
while(T){
Push(S,T);
if(T->Tag){ //遍历左子树
T=T->Left;
if(T)
T->Tag=true;
}
else{ //遍历右子树
T=T->Right;
if(T)
T->Tag=true;
}
}
T=Pop(S);
if(T->Tag) //右子树还没遍历
T->Tag=!T->Tag;
else{ //右子树已经遍历
while(!T->Tag){
cout<<T->data;
if(S->next!=NULL)
T=Pop(S);
else
break;
}
if(T->Tag)
T->Tag=!T->Tag;
else
T=NULL;
}
}
}
(四)层序遍历
按树的层次,从左至右、从上到下进行访问。先遇到的结点先访问,完成某一层结点的访问后,再按它们的访问次序依次访问各结点的左右孩子结点。其具体实现,需要借助队列。
从根结点开始遍历,将根结点指针入队,然后反复执行如下操作,直到队列为空:
从队中取出一个元素;
访问该元素所指结点;
若该元素所指结点的左、右孩子结点非空,则将其左右孩子的指针顺序入队。
有关队列的操作见前文层序创建二叉树部分。
遍历代码如下:
/*层序遍历*/
void LevelorderTraversal(BinTree* &BT)
{
LinkQueue *Q;
Q=new LinkQueue;
CreateQueue(Q);
BinTree *T;
T=BT;
if(!BT)
return;
InsertQueue(Q,T);
while(!IsEmpty(Q)){
T=DeleteQueue(Q);
cout<<T->data;
if(T->Left)
InsertQueue(Q,T->Left);
if(T->Right)
InsertQueue(Q,T->Right);
}
}
四、输出二叉树的叶子结点
按递归的先序遍历算法,在访问结点时,添加if语句判断其是否为叶子结点。
/*打印叶子结点*/
void PreorderPrintLeaves(BinTree *BT)
{
if(BT){
if(!BT->Left && !BT->Right)
cout<<BT->data;
PreorderPrintLeaves(BT->Left);
PreorderPrintLeaves(BT->Right);
}
}
五、求二叉树高度
二叉树的高度为左右子树高度的最大值加一(根结点的高度),稍修改递归的后序遍历算法即可。
/*求二叉树的高度*/
int GetHeight(BinTree *BT)
{
int HL,HR,MaxH;
if(BT){
HL=GetHeight(BT->Left);
HR=GetHeight(BT->Right);
MaxH=HL>HR?HL:HR;
return(MaxH+1);
}
else
return 0;
}
能直接运行的、实现以上全部操作的完整代码:
#include <iostream>
using namespace std;
typedef char ElementType;
/*定义二叉树结点类型*/
typedef struct BinNode
{
ElementType data;
BinNode *Left;
BinNode *Right;
bool Tag;
}BinTree;
/*定义链栈结构*/
typedef struct SNode
{
BinTree *node;
SNode *next;
}LinkStack;
/*定义队列结点类型*/
struct QueNode
{
BinTree *node;
QueNode *next;
};
/*定义队列结点的指针*/
struct LinkQueue
{
QueNode *Front;
QueNode *Rear;
};
BinTree *CreatePre(BinTree* &BT); //先序创建二叉树
void CreateLev(BinTree* &BT); //层序创建二叉树
void PreorderTraversal(BinTree* &BT); //递归的先序遍历
void InorderTraversal(BinTree* &BT); //递归的中序遍历
void PostorderTraversal(BinTree* &BT); //递归的后序遍历
void NoRecuPreorder(BinTree* &BT); //非递归的先序遍历
void NoRecuInorder(BinTree* &BT); //非递归的中序遍历
void NoRecuPostorder(BinTree* &BT); //非递归的后序遍历
void LevelorderTraversal(BinTree* &BT);//层序遍历
void PreorderPrintLeaves(BinTree *BT); //打印叶子结点
int GetHeight(BinTree *BT); //求二叉树高度
int main()
{
BinTree *BT;
CreatePre(BT);
//CreateLev(BT); 层序创建
cout<<"递归先序遍历:"<<endl;
PreorderTraversal(BT);
cout<<endl;
cout<<"非递归先序遍历:"<<endl;
NoRecuPreorder(BT);
cout<<endl;
cout<<"递归中序遍历:"<<endl;
InorderTraversal(BT);
cout<<endl;
cout<<"非递归中序遍历:"<<endl;
NoRecuInorder(BT);
cout<<endl;
cout<<"递归后序遍历:"<<endl;
PostorderTraversal(BT);
cout<<endl;
cout<<"非递归后序遍历:"<<endl;
NoRecuPostorder(BT);
cout<<endl;
cout<<"层序遍历:"<<endl;
LevelorderTraversal(BT);
cout<<endl;
cout<<"输出叶子结点:"<<endl;
PreorderPrintLeaves(BT);
cout<<endl;
cout<<"二叉树的高度:"<<endl;
cout<<GetHeight(BT)<<endl;
return 0;
}
/*初始化链栈*/
LinkStack *CreateStack()
{
LinkStack *S;
S=new LinkStack;
S->next=NULL;
return(S);
}
/*入栈:将二叉树结点压入栈顶*/
void Push(LinkStack *S,BinTree *X)
{
LinkStack *P;
P=new LinkStack;
P->node=X;
P->next=S->next;
S->next=P;
}
/*出栈:将二叉树结点从栈顶弹出*/
BinTree *Pop(LinkStack *S)
{
LinkStack *P;
BinTree *T;
if(S->next==NULL)
T=NULL;
else{
P=S->next;
T=P->node;
S->next=P->next;
delete P;
}
return(T);
}
/*初始化队列*/
void CreateQueue(LinkQueue *Q)
{
QueNode *Node;
Node=new QueNode;
Node->next=NULL;
Q->Front=Node;
Q->Rear=Node;
}
/*判断队列是否为空*/
bool IsEmpty(LinkQueue *Q)
{
return(Q->Front->next==NULL && Q->Rear->next==NULL);
}
/*入队:将二叉树结点插入队中*/
void InsertQueue(LinkQueue *Q,BinTree *X)
{
QueNode *P;
P=new QueNode;
P->node=X;
P->next=NULL;
if(IsEmpty(Q))
Q->Front->next=Q->Rear=P;
else{
Q->Rear->next=P;
Q->Rear=P;
}
}
/*出队:返回并删除队头结点*/
BinTree *DeleteQueue(LinkQueue *Q)
{
QueNode *P;
BinTree *X;
if(IsEmpty(Q)){
cout<<"队空"<<endl;
return NULL;
}
else{
P=Q->Front->next;
X=P->node;
Q->Front->next=P->next;
if(Q->Rear==P)
Q->Rear=Q->Front;
delete P;
return(X);
}
}
/*按先序遍历创建二叉树*/
BinTree *CreatePre(BinTree* &BT)
{
char ch;
cout<<"ch=";
cin>>ch;
if(ch=='0')
BT=NULL;
else{
BT=new BinTree;
BT->data=ch;
BT->Left=CreatePre(BT->Left);
BT->Right=CreatePre(BT->Right);
}
return(BT);
}
/*层序创建二叉树*/
void CreateLev(BinTree* &BT)
{
BinTree *T;
LinkQueue *Q;
Q=new LinkQueue;
CreateQueue(Q);
char ch;
cout<<"ch=";
cin>>ch;
if(ch!='0'){
BT=new BinTree;
BT->data=ch;
BT->Left=NULL;
BT->Right=NULL;
InsertQueue(Q,BT);
}
else
return;
while(!IsEmpty(Q)){
T=DeleteQueue(Q);
cout<<"ch=";
cin>>ch;
if(ch!='0'){
T->Left=new BinTree;
T->Left->data=ch;
T->Left->Left=T->Left->Right=NULL;
InsertQueue(Q,T->Left);
}
else
T->Left=NULL;
cout<<"ch=";
cin>>ch;
if(ch!='0'){
T->Right=new BinTree;
T->Right->data=ch;
T->Right->Left=T->Right->Right=NULL;
InsertQueue(Q,T->Right);
}
else
T->Right=NULL;
}
}
/*递归先序遍历*/
void PreorderTraversal(BinTree* &BT)
{
if(BT){
cout<<BT->data;
PreorderTraversal(BT->Left);
PreorderTraversal(BT->Right);
}
}
/*递归中序遍历*/
void InorderTraversal(BinTree* &BT)
{
if(BT){
InorderTraversal(BT->Left);
cout<<BT->data;
InorderTraversal(BT->Right);
}
}
/*递归后序遍历*/
void PostorderTraversal(BinTree* &BT)
{
if(BT){
PostorderTraversal(BT->Left);
PostorderTraversal(BT->Right);
cout<<BT->data;
}
}
/*非递归先序遍历*/
void NoRecuPreorder(BinTree* &BT)
{
BinTree *T;
LinkStack *S;
S=CreateStack();
T=BT;
while(T || S->next!=NULL){
while(T){
cout<<T->data;
Push(S,T);
T=T->Left;
}
T=Pop(S);
T=T->Right;
}
}
/*非递归中序遍历*/
void NoRecuInorder(BinTree* &BT)
{
BinTree *T;
LinkStack *S;
S=CreateStack();
T=BT;
while(T || S->next!=NULL){
while(T){
Push(S,T);
T=T->Left;
}
T=Pop(S);
cout<<T->data;
T=T->Right;
}
}
/*非递归后序遍历*/
void NoRecuPostorder(BinTree* &BT)
{
BinTree *T;
LinkStack *S;
S=CreateStack();
T=BT;
T->Tag=true;
while(T || S->next!=NULL){
while(T){
Push(S,T);
if(T->Tag){ //遍历左子树
T=T->Left;
if(T)
T->Tag=true;
}
else{ //遍历右子树
T=T->Right;
if(T)
T->Tag=true;
}
}
T=Pop(S);
if(T->Tag) //右子树还没遍历
T->Tag=!T->Tag;
else{ //右子树已经遍历
while(!T->Tag){
cout<<T->data;
if(S->next!=NULL)
T=Pop(S);
else
break;
}
if(T->Tag)
T->Tag=!T->Tag;
else
T=NULL;
}
}
}
/*层序遍历*/
void LevelorderTraversal(BinTree* &BT)
{
LinkQueue *Q;
Q=new LinkQueue;
CreateQueue(Q);
BinTree *T;
T=BT;
if(!BT)
return;
InsertQueue(Q,T);
while(!IsEmpty(Q)){
T=DeleteQueue(Q);
cout<<T->data;
if(T->Left)
InsertQueue(Q,T->Left);
if(T->Right)
InsertQueue(Q,T->Right);
}
}
/*打印叶子结点*/
void PreorderPrintLeaves(BinTree *BT)
{
if(BT){
if(!BT->Left && !BT->Right)
cout<<BT->data;
PreorderPrintLeaves(BT->Left);
PreorderPrintLeaves(BT->Right);
}
}
/*求二叉树的高度*/
int GetHeight(BinTree *BT)
{
int HL,HR,MaxH;
if(BT){
HL=GetHeight(BT->Left);
HR=GetHeight(BT->Right);
MaxH=HL>HR?HL:HR;
return(MaxH+1);
}
else
return 0;
}
可用此层序构建一棵表达式树,程序运行结果如下:
红框以外为手动输入。
可知,表达式树中,叶结点是运算数,非叶结点是运算符。