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

来源:互联网 发布:私人订制软件 编辑:程序博客网 时间:2024/06/10 22:36

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

按照某种遍历方式对二叉树进行遍历,可以把二叉树中所有结点排序为一个线性序列。在改序列中,除第一个结点外每个结点有且仅有一个直接前驱结点;除最后一个结点外每一个结点有且仅有一个直接后继结点。这些指向直接前驱结点和指向直接后续结点的指针被称为线索(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);//递归右子树}



有哪里写的不对请大家指出来,不明白的也可以留言问我,互相学习!
1 0