什么是线索二叉树:
二叉树是一种非线性结构,遍历二叉树几乎都是通过递归或者用栈辅助实现非递归的遍历。用二叉树作为存储结构时,取到一个节点,只能获取节点的左孩子和右孩子,不能直接得到节点的任一遍历序列的前驱或者后继。
为了保存这种在遍历中需要的信息,我们利用二叉树中指向左右子树的空指针来存放节点的前驱和后继信息
摘要:
对于n个结点的二叉树,在二叉链存储结构中有n+1个空链域,利用这些空链域存放在某种遍历次序下该结点的前驱结点和后继结点的指针,这些指针称为线索,加上线索的二叉树称为线索二叉树。
初始化:
二叉树的遍历本质上是将一个复杂的非线性结构转换为线性结构,使每个结点都有了唯一前驱和后继(第一个结点无前驱,最后一个结点无后继)。对于二叉树的一个结点,查找其左右子女是方便的,其前驱后继只有在遍历中得到。为了容易找到前驱和后继,有两种方法。一是在结点结构中增加向前和向后的指针fwd和bkd,这种方法增加了存储开销,不可取;二是利用二叉树的空链指针。现将二叉树的结点结构重新定义如下:
//#include<bits/stdc++.h>
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<string>
#include<math.h>
using namespace std ;
typedef long long ll;
#define MAXN 100005
#define INF 0x3f3f3f3f
#define MALL (Bithrnode *)malloc(sizeof(Bithrnode));
typedef struct Bithrnode
{
char data;
struct Bithrnode *lchild, *rchild;
int Ltag, Rtag;
}Bithrnode, *Bitree;
Bitree pre = NULL;//pre为空
Ltag, Rtag分别为该结点的直接前驱和直接后继,当其为1时说明有前驱或者后继的线索化,而当其为0时,则代表该结点有左儿子或右儿子。
建立线索二叉树,或者说对二叉树线索化,实质上就是遍历一棵二叉树。在遍历过程中,访问结点的操作是检查当前的左,右指针域是否为空,将它们改为指向前驱结点或后续结点的线索。为实现这一过程,设指针pre始终指向刚刚访问的结点,即若指针p指向当前结点,则pre指向它的前驱,以便设线索。
另外,在对一颗二叉树加线索时,必须首先申请一个头结点,建立头结点与二叉树的根结点的指向关系,对二叉树线索化后,还需建立最后一个结点与头结点之间的线索。
下面是建立中序二叉树的递归算法,其中pre为全局变量。
首先是先序遍历下创建一颗二叉树:
Bitree CreatBitree(Bitree &T)
{
char ch;
cin >> ch;
if(ch == '#')//若ch为 # ,则二叉树为空树。
T = NULL;
else
{
T = (Bithrnode *)malloc(sizeof(Bithrnode));
T->data = ch;
CreatBitree(T->lchild);//递归先序创建左子树
CreatBitree(T->rchild);//递归先序创建右子树
}
return T;
}
现在我们开始线索化二叉树:
void InThreeding(Bitree p)
{//pre为全局变量,初始化时其右儿子为空,便于在树的最左点开始建线索
if(p)
{
InThreeding(p->lchild); //左子树递归线索化
if(!p->lchild)
{
p->Ltag = 1; //给p加上左线索
p->lchild = pre; //p的左儿子指向它 的前驱pre
}
else
p->Ltag = 0;
if(!pre->rchild) //同理
{
pre->Rtag = 1;
pre->rchild = p;
}
else
pre->Rtag = 0;
pre = p;
InThreeding(p->rchild); //右子树递归线索化
}
}
// 创建头结点,形成循环链表
void Inorderthreading(Bitree &thrt, Bitree T)//thrt指向头结点
{
thrt = MALL;
thrt->Ltag = 0;
thrt->Rtag = 1;
thrt->rchild = thrt; //初始化时右指针指向自己
if(!T)
thrt->lchild = thrt;//树为空则左指针也指向自己
else
{
thrt->lchild = T; //头结点的左儿子指向根节点,pre初始化指向头结点
pre = thrt;
InThreeding(T);
pre->rchild = thrt; //处理完后pre为最右节点,固因指向头结点。
pre->Rtag = 1;
thrt->rchild = pre; //头结点指回最右结点pre
}
}
中序遍历线索二叉树:
void Cout(Bitree T)
{
Bitree p; //T指向头结点,头结点的左链lchild指向根结点
p = T->lchild;
while(p!=T) //当指针p回到头结点时结束
{
while(p->Ltag == 0)
p = p->lchild; //沿左儿子往下
cout << p->data; //访问其左儿子为空的结点
while(p->Rtag && p->rchild!=T)
{ //沿右线索访问后继结点
p = p->rchild;
cout << p->data;
}
p = p->rchild; //转向p的右子树。
}
cout << '\n';
}
(先序以后写趴。。。。)
完整版:
//#include<bits/stdc++.h>
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<string>
#include<math.h>
using namespace std ;
typedef long long ll;
#define MAXN 100005
#define INF 0x3f3f3f3f
#define MALL (Bithrnode *)malloc(sizeof(Bithrnode));
typedef struct Bithrnode
{
char data;
struct Bithrnode *lchild, *rchild;
int Ltag, Rtag;
} Bithrnode, *Bitree;
Bitree pre = NULL;
void first(Bitree T)
{
if(T)
{
cout << T->data;
first(T->lchild);
first(T->rchild);
}
}
void middle(Bitree T)
{
if(T)
{
middle(T->lchild);
cout << T->data;//这里你可以选择保存数据,,,
middle(T->rchild);
}
}
void END(Bitree T)
{
if(T)
{
END(T->lchild);
END(T->rchild);
cout << T->data;
}
}
Bitree CreatBitree(Bitree &T)
{
char ch;
cin >> ch;
if(ch == '#')//若ch为 # ,则二叉树为空树。
T = NULL;
else
{
T = (Bithrnode *)malloc(sizeof(Bithrnode));
T->data = ch;
CreatBitree(T->lchild);//递归先序创建左子树
CreatBitree(T->rchild);//递归先序创建右子树
}
return T;
}
void InThreeding(Bitree p)
{
//pre为全局变量,初始化时其右儿子为空,便于在树的最左点开始建线索
if(p)
{
InThreeding(p->lchild); //左子树递归线索化
if(!p->lchild)
{
p->Ltag = 1; //给p加上左线索
p->lchild = pre; //p的左儿子指向它 的前驱pre
}
else
p->Ltag = 0;
if(!pre->rchild) //同理
{
pre->Rtag = 1;
pre->rchild = p;
}
else
pre->Rtag = 0;
pre = p;
InThreeding(p->rchild); //右子树递归线索化
}
}
void Inorderthreading(Bitree &thrt, Bitree T)//thrt指向头结点
{
thrt = MALL;
thrt->Ltag = 0;
thrt->Rtag = 1;
thrt->rchild = thrt; //初始化时右指针指向自己
if(!T)
thrt->lchild = thrt;//树为空则左指针也指向自己
else
{
thrt->lchild = T; //头结点的左儿子指向根节点,pre初始化指向头结点
pre = thrt;
InThreeding(T);
pre->rchild = thrt; //处理完后pre为最右节点,固因指向头结点。
pre->Rtag = 1;
thrt->rchild = pre; //头结点指回最右结点pre
}
}
void Cout(Bitree T)
{
Bitree p; //T指向头结点,头结点的左链lchild指向根结点
p = T->lchild;
while(p!=T) //当指针p回到头结点时结束
{
while(p->Ltag == 0)
p = p->lchild; //沿左儿子往下
cout << p->data; //访问其左儿子为空的结点
while(p->Rtag && p->rchild!=T)
{
//沿右线索访问后继结点
p = p->rchild;
cout << p->data;
}
p = p->rchild; //转向p的右子树。
}
cout << '\n';
}
void Copy(Bitree T, Bitree &newT)
{
if(T == NULL)
{
newT = NULL;
return ;
}
else
{
newT = (Bithrnode *)malloc(sizeof(Bithrnode));
newT->data = T->data;
Copy(T->lchild, newT->lchild);
Copy(T->rchild, newT->rchild);
}
}
int depth(Bitree T)
{
int m, n;
if(T == NULL)
return 0;
else
{
m = depth(T->lchild);
n = depth(T->rchild);
return m>n ? m+1 : n+1;
}
}
int Nodecount(Bitree T)
{
if(T == NULL)//如果是空树,则结点个数为0,递归结束
return 0;
else
return Nodecount(T->lchild)+Nodecount(T->rchild)+1;
// 否则结点个数为左子树的结点个数+右子树的结点个数+1(根结点)
}
int yezi(Bitree T)
{
int x = Nodecount(T);
if(x > 1)
if(T == NULL)
return 0;
else if(T->lchild == NULL && T->rchild == NULL)//左右为空,证明的叶子结点,返回1.
return 1;
else
return yezi(T->lchild)+yezi(T->rchild);
else
return 0;
}
int main()
{
cout << "请你按先序遍历创建一颗二叉树" << '\n';
Bitree T;
T = CreatBitree(T);
cout << "中序遍历的结果为:" << '\n';
middle(T);
cout << '\n';
int x = yezi(T), y = depth(T), z = Nodecount(T);
Bitree head;
cout << "线索化二叉树后,再遍历输出:" << '\n';
// InThreeding(T);
Inorderthreading(head, T);
Cout(head);
// cout << "其叶子结点为:" << '\n';
// cout << x << ' ' << y << ' ' << z << '\n';
return 0;
}