【数据结构·考研】后序线索二叉树

之前我们介绍了中序线索二叉树的构造、遍历、寻找前趋和寻找后继;先序线索二叉树的构造、遍历、寻找后继。先序线索二叉树无法解决寻找前趋的任务,中序线索二叉树既可以寻找前趋结点也可以寻找后继结点。

今天我们再来看一下后序线索二叉树的构造:

首先是结构声明:

我们将左线索和右线索初始化为0。

/*
结构声明 
*/ 
typedef struct node{
	struct node* left;
	int ltag = 0;
	char val;
	int rtag = 0;
	struct node* right;
}TreeNode,*Tree; 

后序构造线索二叉树的递归代码:

与中序构建线索二叉树的代码基本相同,把对当前结点的操作放到了遍历左右子树之后。

void PostThread(Tree& t, TreeNode* &pre){
	//pre指针指向t的中序前驱,在主函数中预设为NULL
	if(t != NULL){
		PostThread(t->left, pre); //左子树线索化
		PostThread(t->right, pre); //右子树线索化 
		if(t->left == NULL){ //建立当前结点的前驱线索 
			t->left = pre;
			t->ltag = 1;
		}
		if(pre && pre->right == NULL){ //建立前驱结点pre的后继线索 
			pre->right = t;
			pre->rtag = 1;
		}
		pre = t;
	}
} 

继续包装一下递归函数,与中序和先序不同的地方在于最后的pre指针指向根节点,所以不需要收尾。

/*
通过后序遍历建立后序线索二叉树
*/ 
void CreateThread(Tree& t){
	TreeNode* pre = NULL; //前驱指针
	if(t != NULL){
		PostThread(t,pre);
	}	
} 

后序线索二叉树访问前趋:

/*
返回结点p在线索二叉树中的后序序列下的前驱结点
如果有右孩子,则前驱是右孩子,如果没有右孩子,则前驱是左线索
*/
TreeNode* predecessor(TreeNode* p){
	if(p->rtag == 0) return p->right; 
	return p->left;
} 

后序线索二叉树不能很好的解决寻找后继。建立好后序线索二叉树之后,我们就可以根据线索和左右孩子指针以线性的时间来逆向遍历后序线索二叉树了。

/*
后序线索二叉树不能很好的解决寻找后继。
这里给出一直求前驱得到的后序遍历的逆序列 
*/ 
void PostOrder(TreeNode* t){
	TreeNode*p = t;
	for(p; p!= NULL; p = predecessor(p))
		cout<< p->ltag <<" "<< p->val <<" "<< p->rtag <<" ";
}

 我们可以层次遍历一下,看看树打上标记后的样子。

/*
层次遍历
因为线索二叉树的叶子节点几乎没有了空指针,所以进队的条件应该做修改,用 ltag、rtag 来判断
*/ 
void levelOrderTraverse(Tree& t){
	if(t == NULL) return;
	queue<TreeNode*> q;
	TreeNode* p;
	q.push(t);
	while(!q.empty()){
		int width = q.size();
		for(int i = 0;i < width;i ++){
			p = q.front();
			q.pop();
			cout<<p->ltag<<" "<<p->val<<" "<<p->rtag<<"  ";
			if(p->ltag == 0) q.push(p->left);
			if(p->rtag == 0) q.push(p->right);
		}
		cout<<endl;
	}
} 

完整代码:

#include<iostream>
#include<queue>
using namespace std;

/*
结构声明 
*/ 
typedef struct node{
	struct node* left;
	int ltag = 0;
	char val;
	int rtag = 0;
	struct node* right;
}TreeNode,*Tree; 
 
 
void PostThread(Tree& t, TreeNode* &pre){
	//pre指针指向t的中序前驱,在主函数中预设为NULL
	if(t != NULL){
		PostThread(t->left, pre); //左子树线索化
		PostThread(t->right, pre); //右子树线索化 
		if(t->left == NULL){ //建立当前结点的前驱线索 
			t->left = pre;
			t->ltag = 1;
		}
		if(pre && pre->right == NULL){ //建立前驱结点pre的后继线索 
			pre->right = t;
			pre->rtag = 1;
		}
		pre = t;
	}
} 
 
/*
通过后序遍历建立后序线索二叉树
*/ 
void CreateThread(Tree& t){
	TreeNode* pre = NULL; //前驱指针
	if(t != NULL){
		PostThread(t,pre);
	}	
} 
 
/*
返回结点p在线索二叉树中的后序序列下的前驱结点
如果有右孩子,则前驱是右孩子,如果没有右孩子,则前驱是左线索
*/
TreeNode* predecessor(TreeNode* p){
	if(p->rtag == 0) return p->right; 
	return p->left;
} 


/*
后序线索二叉树不能很好的解决寻找后继。
这里给出一直求前驱得到的后序遍历的逆序列 
*/ 
void PostOrder(TreeNode* t){
	TreeNode*p = t;
	for(p; p!= NULL; p = predecessor(p))
		cout<< p->ltag <<" "<< p->val <<" "<< p->rtag <<" ";
}

 
/*
层次遍历
因为线索二叉树的叶子节点几乎没有了空指针,所以进队的条件应该做修改,用 ltag、rtag 来判断
*/ 
void levelOrderTraverse(Tree& t){
	if(t == NULL) return;
	queue<TreeNode*> q;
	TreeNode* p;
	q.push(t);
	while(!q.empty()){
		int width = q.size();
		for(int i = 0;i < width;i ++){
			p = q.front();
			q.pop();
			cout<<p->ltag<<" "<<p->val<<" "<<p->rtag<<"  ";
			if(p->ltag == 0) q.push(p->left);
			if(p->rtag == 0) q.push(p->right);
		}
		cout<<endl;
	}
} 
 
/*
先序构造二叉树
*/ 
void CreateTree(Tree& t){
	char x;
	cin>>x;
	if(x == '#') t = NULL; 
	else{
		t = new TreeNode; 
		t->val = x;  
		CreateTree(t->left); 
		CreateTree(t->right); 
	}
} 
 
int main(){
	Tree t;
	CreateTree(t);
	TreeNode* parent;
	/*
	   a b d # # e # # c f # # #
	*/
	CreateThread(t); 
	cout<<"层次遍历:"<<endl;
	levelOrderTraverse(t);
	cout<<"后序遍历的逆序列"<<endl;
	PostOrder(t); 
}

程序运行结果:

 更多代码请参考:手撕考研数据结构(代码汇总)

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jiawen9

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值