创建图示的二叉树,并将其线索化
先序遍历序列为:ABDGECF
线索二叉树的链式结构描述如下:
typedef struct ThreadNode{
char data;
struct ThreadNode *lchild;
struct ThreadNode *rchild;
int ltag,rtag;//左右线索标志位
}ThreadNode,*ThreadTree;
用先序遍历建立二叉树:
void creat_ThreadTree(ThreadTree &T){
char data;
cin>>data;
if(data=='#'){
T=NULL;return;
}else{
T=(ThreadNode *)malloc(sizeof(ThreadNode));
T->data=data;
T->lchild=NULL;
T->rchild=NULL;
T->ltag=0;
T->rtag=0;
creat_ThreadTree(T->lchild);
creat_ThreadTree(T->rchild);
}
}
线索化过程:
先定义一个全局变量pre指针,在访问到p节点时,pre指针指向中序遍历序列下p的前一个节点;
中序遍历每一个节点,边遍历边线索化,如果该节点的左(右)孩子指针为NULL,则将该指针指向节点的前驱(后继)节点,直到遍历结束。
值得注意的是,当找pre节点的后继节点p时,先要判断pre!=NULL。这点在代码中会有具体注释说明。
//中序线索化,对每个节点的处理
void visit(ThreadNode *q){
if(q->lchild==NULL){//寻找前驱
q->lchild=pre;
q->ltag=1;//标志位为1,表示q节点的左指针指向的是前驱,而不是孩子
}
if(pre!=NULL&&pre->rchild==NULL){//这两个条件不能写反,因为在pre为NULL情况下访问它的右孩子,会导致程序崩溃
pre->rchild=q;
pre->rtag=1;
}
pre=q;
}
//对整棵树的所有节点做处理
void InThread(ThreadTree T){
if(T!=NULL){
InThread(T->lchild);
visit(T);
InThread(T->rchild);
}
}
void creat_InThread(ThreadTree T){
pre=NULL;
if(T!=NULL){
InThread(T);
if(pre->rchild==NULL)
pre->rtag=1;//最后一个节点要单独做处理,后继一定是NULL,标志为1
}
}
中序线索二叉树的遍历:
中序线索二叉树的节点隐含了线索二叉树的前驱和后继信息。在中序线索二叉树中找节点后继规律是:若右标志位rtag==1,则右孩子指针指向的节点就是后继;否则,根据中序遍历"左 根 右"的特点,遍历右子树中第一个访问的节点(右子树中最左下的节点)为其后继。
//1.求中序线索二叉树中序下的第一个节点
ThreadNode *FirstNode(ThreadNode *p){
while(p->ltag==0) p=p->lchild;
return p;
}
//2.求节点p在中序序列下的后继
ThreadNode *NextNode(ThreadNode *p){
if(p->rtag==0) return FirstNode(p->rchild);//p的右子树的左下角的元素就是它的后继
else return p->rchild;
}
在中序线索二叉树中找节点后继规律是:若左标志位ltag ==1,则左孩子指针指向的节点就是前驱;否则,遍历左子树中最后一个被访问的节点(左子树最右下节点)为其前驱。
//3.找到中序线索二叉树中序遍历的最后一个节点
ThreadNode *LastNode(ThreadNode *p){
while(p->rtag==0) p=p->rchild;
return p;
}
//4.求节点p在中序遍历下的前驱
ThreadNode *PreNode(ThreadNode *p){
if(p->ltag==0) return LastNode(p->lchild);//p的左子树右下角元素是它的前驱
else return p->lchild;
}
根据找后继算法,可以从中序遍历的第一个节点开始,进行找后继遍历,这样可以顺序访问中序遍历下的每一个节点;
根据找前驱算法,可以从中序遍历的最后一个节点开始,进行找前驱遍历,这样可以逆序访问中序遍历下的每一个节点。
完整代码:
#include <cstdio>
#include <cstdlib>
#include<iostream>
#include <type_traits>
using namespace std;
typedef struct ThreadNode{
char data;
struct ThreadNode *lchild;
struct ThreadNode *rchild;
int ltag,rtag;//左右线索标志位
}ThreadNode,*ThreadTree;
ThreadNode *pre=NULL;//定义全局变量
//先建立一棵二叉树
void creat_ThreadTree(ThreadTree &T){
char data;
cin>>data;
if(data=='#'){
T=NULL;return;
}else{
T=(ThreadNode *)malloc(sizeof(ThreadNode));
T->data=data;
T->lchild=NULL;
T->rchild=NULL;
T->ltag=0;
T->rtag=0;
creat_ThreadTree(T->lchild);
creat_ThreadTree(T->rchild);
}
}
//中序线索化,对每个节点的处理
void visit(ThreadNode *q){
if(q->lchild==NULL){//寻找前驱
q->lchild=pre;
q->ltag=1;//标志位为1,表示q节点的左指针指向的是前驱,而不是孩子
}
if(pre!=NULL&&pre->rchild==NULL){//这两个条件不能写反,因为在pre为NULL情况下访问它的右孩子,会导致程序崩溃
pre->rchild=q;
pre->rtag=1;
}
pre=q;
}
//对整棵树的所有节点做处理
void InThread(ThreadTree T){
if(T!=NULL){
InThread(T->lchild);
visit(T);
InThread(T->rchild);
}
}
void creat_InThread(ThreadTree T){
pre=NULL;
if(T!=NULL){
InThread(T);
if(pre->rchild==NULL)
pre->rtag=1;//最后一个节点要单独做处理,后继一定是NULL,标志为1
}
}
//中序线索二叉树遍历
//1.求中序线索二叉树中序下的第一个节点
ThreadNode *FirstNode(ThreadNode *p){
while(p->ltag==0) p=p->lchild;
return p;
}
//2.求节点p在中序序列下的后继
ThreadNode *NextNode(ThreadNode *p){
if(p->rtag==0) return FirstNode(p->rchild);//p的右子树的左下角的元素就是它的后继
else return p->rchild;
}
void Outp(ThreadNode *i){
cout<<i->data<<" ";
}
//利用上面1、2算法可以对中序线索二叉树进行遍历
void Inorder(ThreadNode *T){
for(ThreadNode *i=FirstNode(T);i!=NULL;i=NextNode(i))
Outp(i);
}
//3.找到中序线索二叉树中序遍历的最后一个节点
ThreadNode *LastNode(ThreadNode *p){
while(p->rtag==0) p=p->rchild;
return p;
}
//4.求节点p在中序遍历下的前驱
ThreadNode *PreNode(ThreadNode *p){
if(p->ltag==0) return LastNode(p->lchild);//p的左子树右下角元素是它的前驱
else return p->lchild;
}
//利用上面3、4算法可对中序线索二叉树进行逆向遍历
void RevInorder(ThreadNode *T){
for(ThreadNode *i=LastNode(T);i!=NULL;i=PreNode(i))
Outp(i);
}
int main(){
ThreadTree T;
creat_ThreadTree(T);
creat_InThread(T);
cout<<"中序顺序遍历结果:"<<endl;
Inorder(T);
puts("");
cout<<"中序逆序遍历结果:"<<endl;
RevInorder(T);
return 0;
}
输出结果: