Threaded binary tree

1. Why do you want to thread a binary tree?
An ordinary binary tree can only find the information of the left and right children of the node, and cannot know the direct predecessor or successor information of the node. This kind of information can only be obtained in the process of dynamic traversal, so we introduce a clue binary tree to save the information of predecessors and successors obtained in these dynamic processes. For a binary tree with n nodes, there must be n+1 null pointer fields. We can make full use of these null pointer fields to save the predecessor and successor information of the current node.
Here, we introduce two flag bits, LINK and THREAD. If the left child of the current node is NULL, let the current node point to the predecessor of the node, otherwise continue to traverse down until the left child is empty and end, at this time need Change the flag bit corresponding to the node to THREAD; then traverse the right child of the node, if the right child of the current node is empty, let the node point to the successor of the node, and set the flag bit of the node to THREAD
down Let's study the structural information of nodes

enum PointerTag
{
    THREAD,     //表示该节点需要被线索化
    LINK          //表示该节点不需要进行线索化,继续向下遍历
};
template<class T>
struct BinaryTreeNodeThd
{
    T _data;
    BinaryTreeNodeThd<T>*  _left;
    BinaryTreeNodeThd<T>*  _right;
    PointerTag _leftTag;
    PointerTag _rightTag;
    BinaryTreeNodeThd(const T& x)
        :_data(x)
        , _left(NULL)
        , _right(NULL)
        , _leftTag(LINK)  //在定义一个树的节点时,树的左右孩子都没有被初始化,所以标记为都是LINK
        , _rightTag(LINK)
    {}
};

Compared with the node structure of the binary tree, the node structure of the
write picture description here
clue The concept of clueization: the process of traversing the binary tree in a certain order to turn it into a clue binary tree is called clueization.
2. Constructing a Threaded Binary
Tree The essence of the threaded binary tree construction is to change the null pointer in the binary linked list to point to the predecessor or successor of the node when traversing, and the information of the predecessor and successor can only be obtained when traversing, In order to record the order of visiting nodes during the traversal process, we can set a Prev pointer to record the last node just visited, and root represents the node being visited. Take the in-order as an example to analyze the process of threading:
write picture description here
the access order of the in-order is: first visit the left subtree, then visit the root node, and finally visit the right subtree
//in-order threading
(1) Use Prev to represent the previous one The node that has just appeared, so that it is easy to find the predecessor of the node
(2) How to know the next node to appear?
In in-order traversal, you never know who the next in-order node is, but if you reach the next node, you must know who the previous node is. If the right of the previous node is empty, it means that the right of the previous node is empty. Need to be clued, let the right of the previous node point to the current node. For example, when reaching node 2, the previous node of node 2 is node 3, and the right child of node 3 is empty, so let the right child of node 3 point to node 2
Let's see how to implement it with code

in-order threading

void _InOrderThd(Node* root,Node* &Prev)//出现左树为空或者右树为空,就需要线索化
    {
        if (root == NULL)
            return;
        _InOrderThd(root->_left,Prev);
        //root在这出现的顺序就是中序  上一层结构设置的栈帧,用引用
        if (root->_left == NULL)
        {
            root->_left = Prev;
            root->_leftTag = THREAD;
        }
        if (Prev&&Prev->_right==NULL)
        {
            Prev->_right = root;
            Prev->_rightTag = THREAD;
        }
        Prev = root;
        _InOrderThd(root->_right,Prev);
    }

preorder cueing

void _PrevOrderThd( Node* root, Node* &Prev)//前序线索化
    {
        if (root == NULL)
            return;
        if (root->_left == NULL)
        {
            root->_left = Prev;
            root->_leftTag = THREAD;
        }
        if (Prev&&Prev->_right == NULL)//只有是LINK时才需要继续走
        {
            Prev->_right = root;
            root->_rightTag = THREAD;
        }
        Prev = root;
        if (root->_leftTag == LINK)//避免死循环
        {
            _PrevOrderThd(root->_left, Prev);
        }
        if (root->_rightTag == LINK)
        {
            _PrevOrderThd(root->_right, Prev);
        }
    }

3. Traversing the clue binary tree
Because the binary tree is clued, there is information on the predecessor and successor of the node, so it is easy to find the predecessor and successor information of the current node.
Take the middle sequence as an example:
Find the predecessor of the current node
(1) If the _leftTag of the current node is THREAD, then the current node points to the predecessor of the node
(2) If the _leftTag of the current node is LINK, it means that the node has a left child tree, then the predecessor of the node points to the rightmost subtree of the left child of the node
Find the successor of the current node
(1) If the _rightTag of the current node is THREAD, then the current node points to the successor of the node
(2) If the _rightTag of the current node is THREAD The rightTag is LINK, indicating that the node has a right child, and the successor of the node points to the leftmost node of the right child. Go
down and use the code to implement the in-order traversal of the binary tree

//中序遍历线索化,参考非递归,不需要定义栈
    //中序遍历,遇到一棵树,先找这棵树的最左节点,最先不被访问节点一定是最左节点,
    //当前节点访问完成需要再访问其右树
    //1、如果当前节点的右为LINK,当作子问题
    //2.如果当前节点的左为THREAD,直接跳转
    //最后访问的节点右树一定是空
    void InOrderThread()//用线索化进行遍历
    {
        Node* cur = _root;
        while (cur)
        {
            //先找第一个需要被访问的节点->最左节点
            while (cur->_leftTag == LINK)
            {
                cur = cur->_left;
            }
            cout << cur->_data << " ";
            /*到这里,cur的右树还没有被访问
            1、右为子树
            2、右为THREDAD,一定会跳到当前节点的父亲
            第一种写法*/
            //while (cur->_rightTag==THREAD)
            //{
            //  //跳到后继节点直接访问
            //  cur = cur->_right;
            //  cout<<cur->_data<<" ";
   //         }
            ////子问题c,ur的右树为LINK,子树
            //cur = cur->_right;
            if (cur->_rightTag == LINK)
            {
                cur = cur->_right;
            }
            else
            {
                while (cur->_rightTag == THREAD)
                {
                    cur = cur->_right;
                    cout << cur->_data<<" ";
                }
                cur = cur->_right;
            }
        }
        cout << endl;
    }

All code for preorder, inorder, postorder threading and traversal of binary tree

#pragma once
enum PointerTag
{
    THREAD,
    LINK
};
template<class T>
struct BinaryTreeNodeThd
{
    T _data;
    BinaryTreeNodeThd<T>*  _left;
    BinaryTreeNodeThd<T>*  _right;
    PointerTag _leftTag;
    PointerTag _rightTag;
    BinaryTreeNodeThd(const T& x)
        :_data(x)
        , _left(NULL)
        , _right(NULL)
        , _leftTag(LINK)
        , _rightTag(LINK)
    {}
};
template <class T>
class BinaryTreeThd
{
    typedef BinaryTreeNodeThd<T> Node;
public:

    BinaryTreeThd(T* a, size_t n,const T& invalid)
    {
        size_t index = 0;
        _root = _CreateTree(a, n, invalid, index);
    }
    void PrevOrder()//前序遍历
    {
        _PrevOrder(_root);
        cout << endl;
    }
    void PrevOrderThd()//前序线索化
   {
        Node* Prev = NULL;
        _PrevOrderThd(_root, Prev);
    }
    void InOrderThd()//中序线索化
    {
        Node* Prev =NULL;
        _InOrderThd(_root, Prev);
    }
    //中序遍历线索化,参考非递归,不需要定义栈
    //中序遍历,遇到一棵树,先找这棵树的最左节点,最先不被访问节点一定是最左节点,
    //当前节点访问完成需要再访问其右树
    //1、如果当前节点的右为LINK,当作子问题
    //2.如果当前节点的左为THREAD,直接跳转
    //最后访问的节点右树一定是空
    void InOrderThread()//用线索化进行遍历
    {
        Node* cur = _root;
        while (cur)
        {
            //先找第一个需要被访问的节点->最左节点
            while (cur->_leftTag == LINK)
            {
                cur = cur->_left;
            }
            cout << cur->_data << " ";
            /*到这里,cur的右树还没有被访问
            1、右为子树
            2、右为THREDAD,一定会跳到当前节点的父亲
            第一种写法*/
            //while (cur->_rightTag==THREAD)
            //{
            //  //跳到后继节点直接访问
            //  cur = cur->_right;
            //  cout<<cur->_data<<" ";
   //         }
            ////子问题c,ur的右树为LINK,子树
            //cur = cur->_right;
            if (cur->_rightTag == LINK)
            {
                cur = cur->_right;
            }
            else
            {
                while (cur->_rightTag == THREAD)
                {
                    cur = cur->_right;
                    cout << cur->_data<<" ";
                }
                cur = cur->_right;
            }
        }
        cout << endl;
    }

protected:
    Node* _CreateTree(T* a, size_t n, const T& invalid, size_t &index)//index用引用,递归里面++要给上一层使用
    {
        Node* root = NULL;
        if (index < n && a[index] != invalid)
        {
            root = new Node(a[index]);
            root->_left = _CreateTree(a, n, invalid, ++index);
            root->_right = _CreateTree(a, n, invalid, ++index);
        } 
        return  root;
    }
    //void _PrevOrder(Node* root)//前序遍历,注意线索化之后不能像之前那样遍历
    //{
    //  if (root == NULL)
    //      return;
    //  cout << root->_data<<" ";//访问根节点
    //  _PrevOrder(root->_left);//访问左子树
    //  _PrevOrder(root->_right);//访问右子树
    //}
    void _PrevOrder(Node* root)//前序遍历,注意线索化之后不能像之前那样遍历
    {
        if (root == NULL)
            return;
        cout << root->_data << " ";//访问根节点
        if (root->_leftTag == LINK)//避免死循环
        {
            _PrevOrder(root->_left);//访问左子树
        }
        if (root->_rightTag == LINK)
        {
            _PrevOrder(root->_right);//访问右子树
        }
    }
    void _PrevOrderThd( Node* root, Node* &Prev)//前序线索化
    {
        if (root == NULL)
            return;
        if (root->_left == NULL)
        {
            root->_left = Prev;
            root->_leftTag = THREAD;
        }
        if (Prev&&Prev->_right == NULL)//只有是LINK时才需要继续走
        {
            Prev->_right = root;
            root->_rightTag = THREAD;
        }
        Prev = root;
        if (root->_leftTag == LINK)//避免死循环
        {
            _PrevOrderThd(root->_left, Prev);
        }
        if (root->_rightTag == LINK)
        {
            _PrevOrderThd(root->_right, Prev);
        }
    }
    //中序线索化
    //1、用Prev表示上一个出现过的节点
    //2、如何知道下一个出现的节点,在中序遍历中,永远不知道下一个中序出现节点是谁,
    //但是如果到达下一个节点,一定知道上一个出现的节点是谁,如果上一个节点的右是空,说明
    //上一个节点的右需要线索化,让上一个节点的右指向当前节点
    void _InOrderThd(Node* root,Node* &Prev)//出现左树为空或者右树为空,就需要线索化
    {
        if (root == NULL)
            return;
        _InOrderThd(root->_left,Prev);
        //root在这出现的顺序就是中序  上一层结构设置的栈帧,用引用
        if (root->_left == NULL)
        {
            root->_left = Prev;
            root->_leftTag = THREAD;
        }
        if (Prev&&Prev->_right==NULL)
        {
            Prev->_right = root;
            Prev->_rightTag = THREAD;
        }
        Prev = root;
        _InOrderThd(root->_right,Prev);
    }

protected:
    Node *_root;
};
//一棵树只能被线索化成为一种顺序
int main()
{
    int arr[10] = { 1, 2, 3, '#', '#', 4, '#', '#', 5, 6 };
    BinaryTreeThd<int> t1(arr, sizeof(arr) / sizeof(arr[0]), '#');
    t1.PrevOrder();
    t1.InOrderThd();
    t1.InOrderThread();

}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326483148&siteId=291194637