【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);//递归右子树  
}  


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页