线索二叉树其实是把二叉树转化为 双向线性链表
如果需要多次遍历时,可以减少递归次数,节省时间
线索化的顺序就是 遍历的顺序
线索化 == 在递归遍历的过程中修改空指针
完整代码在最下面,需要的兄弟直接翻到最下
1.线索化原理
三个顺序的线索化思想都是一样的,只不过遍历的方式不同。
前序递归时注意判断为link
思想:
过程:
当然,可以添加一个头结点,使第一个点的lchild 和 最后一个点的rchild 指向它。
解决pre指针全局变量的方法:
(1)在C++ private中定义一个 pre指针
(2)在函数里定义一个静态变量 static BiThrTree pre=NULL;
2.查找后继结点
中序:
如果右标志为1 --> 直接找到后继结点
右标志为0 --> 找到右子树的最左结点
//中序找后继结点
BiThrTree findnextnode(BiThrTree p)
{
if(p->rtag==1)
return p->rchild;
else{
BiThrTree q;
q=p->rchild;
while (q->ltag==0) //如果有左孩子,那么继续循环到最左孩子
{
q=q->lchild;
}
return q;
}
}
3.遍历
中序遍历
思路:找到第一个点,while输出 rchild(后继)即可
第一个点就是最左结点
注意:
如果添加了头结点,需要这么写:
int inorder_traverstree(BiThrTree T)
{
BiThrTree p;
p=T->lchild; //根节点为虚拟头结点的左孩子
if(p==NULL)
return 0;
while(p !=T)
{
while (p->ltag ==Link) //找到中序遍历第一个结点,此节无左孩子 结束循环
{
p=p->lchild;
}
printf("%c",p->data);
while(p->rtag ==Thread &&p->rchild!=T) //右孩子不为根节点 循环输出后继结点
{
p=p->rchild;
printf("%c",p->data);
}
p=p->rchild;
}
return 1;
}
后序遍历
后序遍历比较特殊需要用到parent结点
可以看到在上面的二叉树中如果还是像中序遍历那样循环输出p->rchild,那么D和E会陷入死循环。需要我们回溯双亲结点
当 p指向D,pre指向E时,pre=p; p=p->parent; 也就是让p指向B,pre指向D
4.完整代码
后序线索化并输出 完整代码
#include <stdio.h>
#include <stdlib.h>
#define type char
typedef enum {Link, Thread} PointerTag;
typedef struct bithrnode
{
type data;
struct bithrnode *lchild,*rchild,*parent;
PointerTag ltag, rtag;
}BiThrNode, *BiThrTree;
BiThrTree pre; //前驱结点
void creattree1(BiThrTree *T)
{
char ch;
scanf("%c",&ch);
if(ch=='#')
{
*T=NULL;
}
else
{
*T=(BiThrTree)malloc(sizeof(BiThrTree));
if(!*T)
exit(0);
(*T)->data=ch;
(*T)->ltag=0;
(*T)->rchild=0;
creattree1(&(*T)->lchild);
if((*T)->lchild)
{
(*T)->lchild->parent=(*T);
}
creattree1(&(*T)->rchild);
if((*T)->lchild)
{
(*T)->lchild->parent=(*T);
}
}
}
void lathreading(BiThrTree p)
{
if(p)
{
lathreading(p->lchild);
lathreading(p->rchild);
if(p->lchild==NULL)
{
p->ltag=Thread;
p->lchild=pre;
}
if(pre && pre->rchild==NULL)
{
pre->rtag=Thread;
pre->rchild=p;
}
pre=p;
}
}
//后序遍历
//寻找后序遍历的第一个点
BiThrTree getfirdtnode(BiThrTree p)
{
while(p && p->ltag==Link)
{
p=p->lchild;
}
if(p->rtag ==Link)
return getfirdtnode(p->rchild);
return p;
}
//递归调用
void pri(BiThrTree T)
{
if(T==NULL)
return;
pri(T->lchild);
pri(T->rchild);
printf("%c",T->data);
}
void laorder_traverstree(BiThrTree T)
{
BiThrTree p;
BiThrTree root=T;
p=getfirdtnode(T);
while(p)
{
if(p->rtag ==Thread)
{
printf("%c",p->data);
pre=p;
p=p->rchild;
}
else
{
if(p->rchild ==pre) //说明循环了
{
printf("%c",p->data);
if(p == root)
exit(0);
pre=p;
p=p->parent; //跳出循环
}
else //进入结点的右孩子,寻找
{
p=p->rchild;
p=getfirdtnode(p);
}
}
}
}
int main()
{
BiThrTree T;
printf("请输入先序遍历序列\n");
creattree1(&T);
//pri(T);
lathreading(T);
laorder_traverstree(T);
return 0;
}
前序、中序线索化、输出二叉树
#include <stdio.h>
#include <stdlib.h>
#define type char
typedef enum {Link, Thread} PointerTag;
typedef struct bithrnode
{
type data;
struct bithrnode *lchild,*rchild;
PointerTag ltag, rtag;
}BiThrNode, *BiThrTree;
BiThrTree pre; //前驱结点 全局变量,为了递归是数据不丢失
void creattree(BiThrTree *T)
{
char ch;
scanf("%c",&ch);
if(ch =='#')
{
*T=NULL;
}
else
{
*T=(BiThrTree)malloc(sizeof(BiThrNode));
if(!*T)
exit(0);
(*T)->data=ch;
(*T)->ltag=0;
(*T)->rtag=0;
creattree(&(*T)->lchild);
creattree(&(*T)->rchild);
}
}
//先中后线索化差别就在顺序上
//先序线索化
void prethreading(BiThrTree p)
{
if(p!=NULL)
{
if(p->lchild==NULL)
{
p->ltag=Thread;
p->lchild=pre;
}
if(pre && pre->rchild==NULL)
{
pre->rtag=Thread;
pre->rchild=p;
}
pre=p;
if(p->ltag ==Link)
prethreading(p->lchild);
if(p->rtag ==Link) //注意需要判断,否则会死循环
prethreading(p->rchild);
}
}
//中序线索化
void inthreading(BiThrTree p)
{
if(p)
{
inthreading(p->lchild);
if(p->lchild==NULL)
{
p->ltag=Thread;
p->lchild=pre;
}
if(pre && pre->rchild==NULL) //注意在这里一定要判断pre不为空,否则有的编译器不给过
{
pre->rtag=Thread;
pre->rchild=p;
}
pre=p;
inthreading(p->rchild);
}
}
//前序的下一个结点
BiThrTree pre_nexr(BiThrTree p)
{
if(p->ltag==Thread ||p->rtag==Thread)
{
return p=p->rchild;
}
else{
return p=p->lchild;
}
}
//前序遍历
void preorder_traverstree(BiThrTree T)
{
BiThrTree p;
p=T; //第一个结点为根节点
while(p)
{
printf("%c",p->data);
p=pre_nexr(p);
}
}
//中序遍历
int inorder_traverstree(BiThrTree T)
{
BiThrTree p;
p=T;
if(p==NULL)
return 0;
while (p->ltag ==Link) //找到中序遍历第一个结点,此节无左孩子 结束循环
{
p=p->lchild;
}
printf("%c",p->data);
while(p->rchild!=NULL) //右孩子不为根节点 循环输出后继结点
{
p=p->rchild;
printf("%c",p->data);
}
return 1;
}
//中序找后继结点
BiThrTree findnextnode(BiThrTree p)
{
if(p->rtag==1)
return p->rchild;
else{
BiThrTree q;
q=p->rchild;
while (q->ltag==0) //如果有左孩子,那么继续循环到最左孩子
{
q=q->lchild;
}
return q;
}
}
int main()
{
BiThrTree T;
printf("请以前序输入字符串\n");
creattree(&T);
int f=0;
printf("1先序2中序\n");
scanf("%d",&f);
if(f==1)
{
prethreading(T);
preorder_traverstree(T);
printf("\n前序遍历 根节点的后继结点\n");
BiThrNode* t=pre_nexr(T);
printf("%c\n",t->data);
}
else
{
inthreading(T);
inorder_traverstree(T);
printf("\n中序遍历 根节点的后继结点\n");
BiThrNode* t=findnextnode(T);
printf("%c\n",t->data);
}
return 0;
}
这些代码让我改了好久,┭┮﹏┭┮
如果有什么问题可以留言,顺便点个赞,谢谢!