先规定线索二叉树结点结构如下
struct ThreadNode{
element data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;
}ThreadNode,*ThreadTree;
ltag=0 表示lchild指向左孩子
ltag=1 表示lchild指向前驱
rtag=0 表示rchild指向右孩子
rtag=1 表示rchild指向后继
注意:第一号结点前驱为NULL,最后一号结点后继为NULL,其余叶节点lr指针都指向前驱或后继,只要是结点lr指针都有指向(要么前驱后继,要么孩子)
当一个二叉树p由线索节点构成但又尚未线索化时,如何操作才能将p线索化?下面进行分析:
首先对二叉树线索化肯定要遍历一次二叉树,分析每个结点并对应的修改其指针指向行程线索流。用中序遍历来讨论,前序和后续还没思考过……
一、将【遍历顺序的实现】和【对结点的具体操作】分割开分别来看
只要函数的结构满足形式↓
void Preorder(BiTree T){
if(p!=NULL){
Preoder(T->lchild);
visit(T)//visit泛指对当前结点的具体操作,可以是很多条。T即当前的结点
Preoder(T->rchild);
}
}
就能保证处理结点的顺序是按照中序来的,这样在对结点进行具体操作时作时就只用考虑怎么操作能达到目标要求(样式形状anyway……)而不用再管是不是要进到下一个结点顺序对不对(??)。起到遍历顺序和具体操作隔离开的效果。
二、对节点的具体操作
树中的结点状态各不相同(有两个孩子/有左/有右/无孩子),相对应的操作也就不同。
提出两种解决思路:一种是判别结点种类(是上述四种中哪一种)(仅猜想,还没思考过可行性……)
二是不管什么类型,对每一个结点都将所有需要的操作都做
采取法二,抽象出每个结点需要做的操作:
① 检查当前结点lchild是否为空 → 满,则说明有左孩子不用(也不能)构建前驱,【不操作】
→ 空,【构建前驱,p->lchild=pre ; ltag=1】
②检查当前结点的上一结点rchild是否为空→满,则说明有右孩子不用(也不能)构建后继,【不操作】
→空,则将上一结点rchild指向当前结点使当前结点成为其后继(此时pre是插在上一结点)
【pre->rchild=p; rtag=1】
③左右都检查完后将原本插在上一结点的pre更新到当前结点
做完此步后本层(本结点)操作才算完,可以进入下一号结点。
(注意:对每一个结点来说都要进行这三步操作,但检查rchild指针的操作不是在当前这一层做的,是在进到下一个结点的时候又返回来检查的。)
下列是完整代码
void InThread(ThredTree &p,ThreadTree &pre){ //前序线索化函数
if(p!=NULL){
InThread(p->lchild,pre);
if(p->lchild==NULL){
p->lchild=pre;
p->ltag=1;
}
if(pre!=NULL&&pre->rchild==NULL){
pre->rchild=p;
pre->rtag=1;
}
pre=p;
InThread(p->rchild,pre);
}
}
void CreatInThread(ThreadTree T){ //此为主函数,ThreadTree是线索结点的指针型(?不会描述)
ThreadTree pre=NULL;
if(T!=NULL){
InThread(T,pre);
pre->rchild=NULL;//在主函数中把最后一号结点rchild指向NULL
pre->rtag=1;
}
}
小白第一次写博客,请大佬纠错的时候温柔一点