将二叉树线索化,实际上就是将其变为一个循环链表,下面的代码是采用中序的线索化,遍历也是中序遍历,都是基于中序的。
在中序遍历序列中求某一结点的前驱和后继的方法:(1)求某一结点的后继:如果所考虑的结点有右孩子,那么就要从该右孩子开始,顺着右孩子的左孩子域找下去,一直到左孩子域为空为止,最后这个结点就是所考虑结点的后继;如果所考虑的结点没有右孩子,那么就要遍历二叉树。(2)求某一结点前驱:如果所考虑结点有左孩子,那么就要从该左孩子开始,顺着该左孩子的右孩子链域找下去,一直到右孩子的链域为空为止,最后这个结点就是所考虑结点的前驱;然后该结点没有左孩子,那么就要遍历二叉树。
下面代码中还有一个要说的就是用来辅助搜索的Address结构体数组,一开始我本来是乡写一个函数,直接就是在遍历的过程中搜索指定数值的结点,但是有时对有时错,搞了一个下午都不知怎么搞好,就放弃了,采用这个结构体数组的方法,简单一些,那个等以后老师讲的时候再看看老师会怎么实现吧,可能会有灵感。
文件"tree.h"
#include<iostream>
using namespace std;
class ThreadTree;//线索二叉树
class Thrnode
{
private:
int data;
int ltag; //ltag为0:表示lchild为一般二叉树的指针,指向其左子树
int rtag; //ltag为1:表示lchild为指向此结点的前驱的指针,rtag同理
Thrnode *lchild;
Thrnode *rchild;
friend class ThreadTree;
};
int num=0;//记录结点数目
struct Address //辅助搜索
{
int data;
Thrnode *p;
}ad[100];
class ThreadTree
{
private:
Thrnode *root;
public:
ThreadTree()
{
root=0;
}
Thrnode *Get_Root()
{
return root;
}
int Get_data(Thrnode *p)
{
return p->data;
}
Thrnode *Create_Tree(Thrnode *r) //先序建立二叉树
{
int d;
cout<<"输入数据(0代表空):";
cin>>d;
if(d==0)
return 0;
else
{
num++;
r=new Thrnode;
r->ltag=0;
r->rtag=0;
r->data=d;
r->lchild=Create_Tree(r->lchild);
r->rchild=Create_Tree(r->rchild);
}
root=r;
return r;
}
void In_Threading(Thrnode *p,Thrnode *&pre)
{
//以指针p所指向的二叉树进行中序遍历,遍历过程中进行线索化
//pre指针是p的前驱指针
if(p)
{
In_Threading(p->lchild,pre);//左子树线索化
if(!p->lchild) //若p的左子树为空,给p结点加前驱线索
{
p->ltag=1;
p->lchild=pre;
}
else
p->ltag=0;
if(pre && !pre->rchild)
{
pre->rtag=1;
pre->rchild=p;
}
pre=p;
In_Threading(p->rchild,pre); //右子树线索化
}
}
Thrnode *InOrder_Threading(Thrnode *r) //将二叉树线索化,按中序遍历的顺序
{
//遍历二叉树,并将其中序线索化,其中,Thrt指针指向头结点,r指向根节点
Thrnode *pre=NULL;
Thrnode *Thrt;
Thrt=new Thrnode;
Thrt->ltag=0;
Thrt->rtag=1;
Thrt->rchild=Thrt;//初始化时,让头结点的右指针指向自己
if(!r)
Thrt->lchild=Thrt; //若为空树,左指针也指回自己
else
{
Thrt->lchild=r;
pre=Thrt;
In_Threading(r,pre); //pre是r的前驱指针
//从上面的函数出来时,pre指在最后一个结点处
pre->rchild=Thrt;
pre->rtag=1;
//最后一个结点的线索化
Thrt->rchild=pre;
}
cout<<"二叉树线索化 完成~~"<<endl;
return Thrt;
}
void InOrder_Thr(Thrnode *Thrt) //中序遍历线索二叉树
{
//遍历的同时,将相对应的值和结点指针存入结构体,便于搜索
int count=0;
Thrnode *r;
r=Thrt->lchild;
while(r!=Thrt)
{
while(r->ltag==0)
r=r->lchild;
cout<<r->data<<" ";
ad[count].data=r->data;
ad[count].p=r;
count++;
while(r->rtag==1 && r->rchild!=Thrt)
{
r=r->rchild;
cout<<r->data<<" ";
ad[count].data=r->data;
ad[count].p=r;
count++;
}
r=r->rchild;
}
}
Thrnode *Search(int d) //搜索
{
for(int i=0;i<num;i++)
if(ad[i].data==d)
return ad[i].p;
}
void Prior_Thr(Thrnode *Thrt,Thrnode *t,Thrnode *&p) //求结点前驱
{
//p返回结点t的前驱
p=t->lchild;
if(p==Thrt)
{
cout<<"给定的结点是第一个结点,不存在前驱"<<endl;
return ;
}
if(t->ltag==0)
while(p->rtag==0)
p=p->rchild;
}
void Next_Thr(Thrnode *Thrt,Thrnode *t,Thrnode *&p) //求结点后继
{
p=t->rchild;
if(p==Thrt)
{
cout<<"该结点为最后一个结点,无后继"<<endl;
return ;
}
if(p->rtag==0)
while(p->ltag==0)
p=p->lchild;
}
void Insert_Lchild(Thrnode *Thrt,Thrnode *t,int d) //作为左孩子插入
{
/*
将结点值为d的结点插入t后面,作为t的左孩子
如果t本来的无左孩子,那么直接插入即可
如果t有左孩子,那么:
t的左孩子在新的节点q插入后,作为q的左孩子,因此q->ltag=0;
将新结点q作为t的左孩子
求出新的结点q的前驱,修改q的前驱结点的rchild域,使它的后继为q
*/
Thrnode *q=new Thrnode;
q->data=d;
q->lchild=t->lchild;
q->ltag=t->ltag;
q->rchild=t;
q->rtag=1;
t->lchild=q;
t->ltag=0;
if(q->ltag==0)
{
Thrnode *p;
Prior_Thr(Thrt,q,p);
p->rchild=q;
}
//若q->rtag为0,那么就要找出q的前继
cout<<"插入为结点值为:"<<t->data<<"的左孩子成功"<<endl;
num++;
}
void Insert_Rchild(Thrnode *Thrt,Thrnode *t,int d) //作为右孩子插入
{
//思路和上面的左孩子一样
Thrnode *q=new Thrnode;
q->data=d;
q->rchild=t->rchild;
q->rtag=t->rtag;
q->lchild=t;
q->ltag=1;
t->rchild=q;
t->rtag=0;
if(q->rtag==0)
{
Thrnode *p;
Next_Thr(Thrt,q,p);
p->lchild=q;
}
cout<<"插入为结点值:"<<t->data<<"的右孩子成功"<<endl;
num++;
}
};
测试函数"main.cpp"
#include"tree.h"
int main()
{
ThreadTree t;
Thrnode *r=t.Get_Root(),*k;
k=t.Create_Tree(r);
cout<<"_____二叉树线索化_____"<<endl;
Thrnode *Thrt=0;
Thrt=t.InOrder_Threading(k);
if(Thrt)
{
cout<<"中序遍历线索二叉树为:";
t.InOrder_Thr(Thrt);
cout<<endl;
}
int data1,data2;
cout<<"输入你要在值为多少的结点处插入作为其左孩子:";
cin>>data1;
Thrnode *p=0;
p=t.Search(data1);
if(!p)
cout<<"找不到插入点"<<endl;
else
{
cout<<"要插入的结点地址为:"<<p<<endl;
cout<<t.Get_data(p)<<endl;
cout<<"输入你要插入的新结点的值:";
cin>>data2;
cout<<"____将新结点插入为左孩子____"<<endl;
t.Insert_Lchild(Thrt,p,data2);
}
cout<<"中序遍历二叉树为:";
t.InOrder_Thr(Thrt);
cout<<endl;
int data3,data4;
cout<<"输入你要在值为多少的结点处插入作为其右孩子:";
cin>>data3;
Thrnode *f=0;
f=t.Search(data3);
if(!f)
cout<<"找不到插入点"<<endl;
else
{
cout<<"要插入的结点地址为:"<<f<<endl;
cout<<t.Get_data(f)<<endl;
cout<<"输入你要插入的新结点的值:";
cin>>data4;
cout<<"____将新结点插入为右孩子____"<<endl;
t.Insert_Rchild(Thrt,f,data4);
}
cout<<"中序遍历二叉树为:";
t.InOrder_Thr(Thrt);
cout<<endl;
cout<<"结点值为:"<<data1<<"的前驱结点值为:";
Thrnode *s=new Thrnode;
t.Prior_Thr(Thrt,p,s);
cout<<t.Get_data(s)<<endl;
cout<<"结点值为:"<<data3<<"的后继结点值为:";
Thrnode *x=new Thrnode;
f=t.Search(data3);
t.Next_Thr(Thrt,f,x);
cout<<t.Get_data(x)<<endl;
return 0;
}
下面是输出结果,输入依然是 5 3 2 1 0 0 0 4 0 0 8 6 0 7 0 0 9 0 0 生成的二叉树图像在之前那篇二叉树的日志里面有,这里不再贴出
输入数据(0代表空):5
输入数据(0代表空):3
输入数据(0代表空):2
输入数据(0代表空):1
输入数据(0代表空):0
输入数据(0代表空):0
输入数据(0代表空):0
输入数据(0代表空):4
输入数据(0代表空):0
输入数据(0代表空):0
输入数据(0代表空):8
输入数据(0代表空):6
输入数据(0代表空):0
输入数据(0代表空):7
输入数据(0代表空):0
输入数据(0代表空):0
输入数据(0代表空):9
输入数据(0代表空):0
输入数据(0代表空):0
_____二叉树线索化_____
二叉树线索化 完成~~
中序遍历线索二叉树为:1 2 3 4 5 6 7 8 9
输入你要在值为多少的结点处插入作为其左孩子:4
要插入的结点地址为:00382478
4
输入你要插入的新结点的值:12
____将新结点插入为左孩子____
插入为结点值为:4的左孩子成功
中序遍历二叉树为:1 2 3 12 4 5 6 7 8 9
输入你要在值为多少的结点处插入作为其右孩子:7
要插入的结点地址为:00382550
7
输入你要插入的新结点的值:13
____将新结点插入为右孩子____
插入为结点值:7的右孩子成功
中序遍历二叉树为:1 2 3 12 4 5 6 7 13 8 9
结点值为:4的前驱结点值为:12
结点值为:7的后继结点值为:13
Press any key to continue