【c++】二叉树的线索化

什么是二叉树的线索化?或者问什么是线索二叉树?

按照某种遍历方式对二叉树进行遍历,可以把二叉树中所有结点排序为一个线性序列。在改序列中,除第一个结点外每个结点有且仅有一个直接前驱结点;除最后一个结点外每一个结点有且仅有一个直接后继结点。这些指向直接前驱结点和指向直接后续结点的指针被称为线索(Thread),加了线索的二叉树称为线索二叉树。

以上是搜狗百科的一段文字,反正我是没看太懂。简单点,以我的理解,线索二叉树就是充分利用了二叉树结点中的空指针,让它们分别指向本结点的前驱或者后继。既充分利用了资源,又可以让我们方便遍历这棵树。

什么是二叉树结点的空指针?

我们先看一棵树。图中的结点3,4,6的左右指针,结点5的右指针等类似指针都为空指针。

\

什么又叫让这些空指针指向本节点的前驱或者后继呢?

这个问题分为3中情况,前序,中序,后序。例如上图中的树前序遍历序列为1,2,3,4,5,6。这样,我们就可以让结点3的左指针指向它的前驱2,结点3的右指针指向它的后继4。让结点4的左指针指向4的前驱3,让结点4的右指针指向4的后继5。结点5的左不为空,所以不操作。结点5的右为空,让结点5的右指向5的后继6。结点6的左右都为空,让结点6的左指针指向6的前驱5,因为6为最后一个元素,6的后继为空,所以让结点6的右指针指向空。

让我们把指针重置后的图画出来:



类似的,后序与中序我就不再细说,这里把图解给大家,一看便知。



我建议大家亲自动手把这3个图画一下,有奇效!!!!!!!


道理大家都懂了,那么代码该如何写呢? 答案:递归。

线索二叉树的结点定义和普通二叉树的结点定义不一样,我们额外需要两个标志_leftTag和_rightTag。

enum Type
{
	THREAD,//表示指针被线索化
	LINK//表示指针未被线索化
};
template<typename T>
struct BinaryTreeNode
{
	T _data;
	BinaryTreeNode<T> *_left;
	BinaryTreeNode<T> *_right;
	Type _leftTag;//标识左指针
	Type _rightTag;//标识右指针
	BinaryTreeNode(const T& x)//构造函数
		:_data(x)
		, _left(NULL)
		, _right(NULL)
		, _leftTag(LINK)
		, _rightTag(LINK)
	{}

	BinaryTreeNode()//默认构造函数
	{}
};


我们用Type类型的_leftTag来标识左指针,当_leftTag等于THREAD时,表明这个指针已经被线索化(例图中紫色指针)。当_leftTag等于LINK时,表明这个指针没有被线索化,是普通的二叉树指针(例图中红色指针)。

因为在线索化过程中,我们需要让当前结点的前驱指向当前结点,所以我们需要设立一个prev来保存上一次访问的结点。这个prev要么设置成全局变量,要么设置成局部静态变量,切记!

递归代码:

前序

//前序线索化
void _PrevOrder_Thd(Node* _root)
{
        static Node* prev = NULL;
	if (_root)
	{
		if (!_root->_left)
		{
			_root->_leftTag = THREAD;
			_root->_left = prev;
		}
		if (prev && !prev->_right)
		{
			prev->_rightTag = THREAD;
			prev->_right = _root;
		}
		prev = _root;
		if (_root->_leftTag == LINK)
			_PrevOrder_Thd(_root->_left);
		if (_root->_rightTag == LINK)
			_PrevOrder_Thd(_root->_right);
	}
}



中序

//中序线索化
void _InOrder_Thd(Node* _root)
{
	static Node* prev = NULL;
	if (_root)
	{
		if (_root->_leftTag == LINK)
		{
			_InOrder_Thd(_root->_left);
		}
		if (!_root->_left)
		{
			_root->_leftTag = THREAD;
			_root->_left = prev;
		}
		if (prev && !prev->_right)
		{
			prev->_rightTag = THREAD;
			prev->_right = _root;
		}
		prev = _root;
		if (_root->_rightTag == LINK)
		{
			_InOrder_Thd(_root->_right);
		}
	}
}


后序

	//后序线索化
void _PostOrder_Thd(Node* _root)
{
	if (_root == NULL)
	{
		return;
	}
	static Node* prev = NULL;
	_PostOrder_Thd(_root->_left);
	_PostOrder_Thd(_root->_right);
	if (!_root->_left)
	{
		_root->_leftTag = THREAD;
		_root->_left = prev;
	}
	if (prev && !prev->_right)
	{
		prev->_rightTag = THREAD;
		prev->_right = _root;
	}
	prev = _root;
}



最后,来说一下今年的一道面试题,如何将一个二叉树转化成一个有序的双向链表?

在你没学线索化之前,这道题可能很麻烦。但是现在不同了,利用中序线索化的思想可以很快将这道题解出来!

//利用中序线索化思想将搜索二叉树转换成有序的双向链表
void _TreeToList(Node* _root)
{
	static Node* prev = NULL;//设立prev保存上一次访问的结点
	if (_root == NULL)//如果根结点为空,表明树空,直接返回.
	{
		return;
	}
	_TreeToList(_root->_left);//递归左子树
	_root->_left = prev;//让当前结点的左指针指向上一次访问的结点,即前驱。
	if (prev)
	{
		prev->_right = _root;//让prev指向当前结点,构成双向
	}
	prev = _root;//更新prev
	_TreeToList(_root->_right);//递归右子树
}



有哪里写的不对请大家指出来,不明白的也可以留言问我,互相学习!
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值