二叉树线索化算法

二叉树线索化

简述

为什么需要线索二叉树?

  1. 对于普通的二叉树来说,如果随便给出二叉树中的一个结点,让你从这个结点遍历整个二叉树,这是做不到的(其实对于普通的二叉树来说要做到找出一个结点的前驱需要创建两个结点指针变量,在进行二叉树遍历的时候(先、中、后),一个指针指向当前访问的结点,一个指针指向上一个访问的结点,通过判断当前访问的结点是否为需要找前驱的结点,如果是则另一个指针则是所要找的前驱),因为每个二叉树结点都只有左右结点,不能够往回(父结点)追溯,所以是不能够访问整个二叉树上面的剩下来的所有结点的。
  2. 又比如我们给出二叉树中的某个结点,要求你找到此结点的前缀,这时候也是找不到的,因为我们不能够通过其中的一个结点去往回追溯结点。
  3. 我们可以利用线索化的二叉树来进行非递归遍历。
    所以有人就发明了线索化的二叉树,能够满足以上的三个要求。下面我将介绍如何实现上面的两个要求的。

我们以下介绍的内容是基于二叉树中序

如果一个二叉树有n个结点,那么它的链域的数量就为2n个,则我们可以通过n+1个空链域来表示当前结点的前驱或者是后继,当结点的左孩子不为空的时候,左孩子指针指向左孩子,当左孩子为空的时候,我们可以用左孩子指针指向当前结点的前驱结点;同样如果结点的右孩子不为空的时候,右孩子指针指向右孩子,当右孩子为空的好时候,用右孩子指针指向当前结点的后继结点。为了能够区分结点的左右孩子指针指向的是孩子结点还是前驱、后继,我们设置了两个标志位来进行区分:rtag、ltag为1时表示的是前驱或者后继,当rtag、ltag为0时表示的是左右孩子。

线索二叉树存储结构

//创建一个全局变量
//默认为空
bitNode* pre = NULL;

//创建一个树结点
typedef struct BITNODE {
	int data;
	struct BITNODE* lchild, * rchild;
	int ltag, rtag;//当标志位为1时lchild、rchild分别表示前缀、后缀;当标志位为0时lchild、rchild分别表示左右结点。
}bitNode,* bitTree;

二叉树线索化

我们将二叉树线索化就是利用二叉树中的空链域来表示二叉树遍历序列(先、中、后)中的前后缀,就是在对二叉树进行相应遍历的时候进行添加空链域的前后缀。
下面我们介绍中序遍历中的二叉树线索化算法。

  • pre指针首先指向NULL,判断当前结点的左节点是否为空,如果为空则将左节点表示为前驱pre,随后将pre赋值为当前结点,再进行下一结点判断,并将标志位ltag置为1。
  • 因为我们当前二叉树遍历方式为中序遍历,遍历顺序为左、根、右,所以当前结点的后缀应该是下一个遍历的结点子树中第一个访问的元素,所以我们在进行当前结点访问的时候判断pre指针指向的结点的右孩子指针是否为空,如果为空则将当前节点作为pre的后缀赋给pre的右孩子,随后将pre赋值为当前结点,再进行下一结点判断,并将标志位rtag置为1。
  • 我们在遍历二叉树最后一个结点的时候需要将rtag标志位置为1,因为最后一个结点无后继结点。

下面是实现代码:

//创建一个全局变量
//默认为空
bitNode* pre = NULL;

//创建一个树结点
typedef struct BITNODE {
	int data;
	struct BITNODE* lchild, * rchild;
	int ltag, rtag;
}bitNode,* bitTree;

//中序遍历算法
void PreTraverse(bitTree bt)
{
	if (NULL == bt)
		return;

	PreTraverse(bt->lchild);
	Visit(bt);
	PreTraverse(bt->rchild);
}

//中序线索化
void Visit(bitNode* bt)
{
	//当前结点左子树为空则左子树(前驱)直接指向pre
	if (NULL == bt->lchild)
	{
		bt->lchild = pre;
		bt->ltag = 1;
	}
	//当pre不为空且pre的右子树为空则pre的右子树(后驱)指向当前结点
	if (pre != NULL && pre->rchild)
	{
		pre->rchild = bt;
		pre->rtag = 1;
	}
	
	pre = bt;					//将pre置成当前结点
}

void CreatInThread(bitTree bt)
{
	if (NULL == bt)
		return;
	PreTraverse(bt);

	if (NULL == pre->rchild)
		pre->rtag = 1;
}

找到一个结点的中序后驱结点

我们随便给出一个线索化的二叉树中的一个结点,要求找出这个结点的中序后继结点:
如果这个结点的右孩子标志位为1则表示rchild指向了后继结点,所以后继结点为rchild;如果右节点的标志位为0,则表示其有右孩子,那么根据中序遍历的顺序可知,我们所要得到的后继结点为右孩子子树中序遍历的第一个结点。
经过如此处理则可以得到相应的后继结点
代码实现为:

//寻找当前结点孩子结点构成根节点的子树中的第一个遍历的结点
//因为遍历为中序遍历,所以子树中的最右结点为第一个遍历的结点
bitNode* SearchFirstNode(bitNode* bn)
{
	while (bn->lchild != NULL)
		bn = bn->lchild;
	return bn;
}

//找到当前结点的后继节点
bitNode* SearchBackNode(bitNode* bn)
{
	if (bn->rtag == 1) return bn->rchild;
	else
		return SearchFirstNode(bn->rchild);
}

找到一个结点的中序前驱结点

其实找前驱结点和找后驱结点的思想是一样的,所以在这里我就不再重复说明了。

利用线索化二叉树实现二叉树的非递归中序遍历

我们通过以上的步骤得到了线索化的二叉树,现在我们通过一个给定结点找到其后驱,所以当给出二叉树的根结点的时候我们即可得到中序遍历的二叉树结果:

//寻找当前结点孩子结点构成根节点的子树中的第一个遍历的结点
bitNode* SearchFirstNode(bitNode* bn)
{
	while (bn->lchild != NULL)
		bn = bn->lchild;
	return bn;
}

//找到当前结点的后继节点
bitNode* SearchBackNode(bitNode* bn)
{
	if (bn->rtag == 1) return bn->rchild;
	else
		return SearchFirstNode(bn->rchild);
}

//单个结点输出
void VisitNode(bitNode* bn)
{
	cout << bn->data << endl;
}
//利用以上结果进行中序遍历的非递归算法
void NoRecurTraveral(bitTree bt)
{
	for (bitNode* p = SearchFirstNode(bt); p != NULL; p = SearchBackNode(p))
		VisitNode(p);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值