创建线索二叉树——中序线索二叉树的遍历(C++实现)

创建图示的二叉树,并将其线索化

先序遍历序列为: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;
}

输出结果:

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值