Thread Binary Tree Learning Notes

Principles of Threaded Binary Trees

There are more idle pointers in the binary tree. A binary tree with n nodes, if stored as a binary chain table, has a total of 2n pointers, of which only (n-1) are used to point to child nodes, and (n+1) are empty and can be used to point to precursor and subsequent nodes in traversal

Therefore, a method is presented to store pointers in the empty chain domain and point to other nodes in the tree. This pointer is called a thread.

    Note that ptr points to a node in the binary chain table. Here are the rules for building clues:

    (1) If ptr->lchild is empty, the precursor nodes pointing to this node in the middle traversal sequence are stored. This node is called the intermediate precursor of ptr;

    (2) If ptr->rchild is empty, the subsequent nodes pointing to the node in the middle traversal sequence are stored. This node is called the intermediate successor of ptr;

How do I distinguish a pointer to a child's node from a pointer to a clue?

Add two fields to the node, ltag and rtag.

(1) When the left (or right) pointer of k points to the precursor (or successor), set the ltag (or rtag) of K to 1.

(2) When they point to the true child nodes, k's ltag (or rtag) is zero.

(3) The left (right) pointer of k is empty only if k is the top (back) node in the middle order, and the ltag (rtag) value of k is 0.

  Binary Thread Tree Storage Structure Definition

/* Binary Thread Storage Structure Definition for Binary Trees*/
template <class T>
struct node {
	T data;                                            //Node data
	node<T>* lchild, * rchild;                         //Left and right child pointer
	int ltag, rtag;                                    //Left and right sign
};
node<class T> BitNode,*BiTree;

Implementation of Binary Thread Tree

  The essence of threading is to change a null pointer in a binary list to a leading or succeeding thread. Since the precursor and subsequent information is only available when the binary tree is traversed, the threading process is the process of modifying the null pointer during traversal.

Intermediate threading process:

//Ordered traversal for ordered threading
void InThreading(BiTree p,BiTree &pre)//PrePoint to pre vious access node, p current access node
{
    if(p)//p!= Execute when NULL
    {
        InThreading(p->lchild,pre);          //Recursive left subtree threading
                //===
        if(!p->lchild)           //No left child
        {
            p->ltag = 1;    //Leading Threads
            p->lchild = pre; //Left child pointer to front
        }
        if(!pre->rchild)     //No Right Child
        {
            pre->rtag = 1;  //Subsequent clues
            pre->rchild = p; //Front right child pointer to next (current node p)
        }
        pre = p;
                //===
        InThreading(p->rchild,pre);      //Recursive right subtree threading
    }
}

As you can see, this code is almost identical to traversing a binary tree in order, with the addition of /==/enclosed parts

What this part of the code does is change the null pointer to sequential and sequential.

First if(!P->lchild)   If not, ltag is labeled 1 and lchild points to the previous visiting node, pre;

After that, if (!pre->rchild), if (!pre->rchild) is judged, if empty, rtag is labeled 1, and rchild points to the next visiting node p, since the succession to P has not yet been accessed (subsequent work of marking P will be implemented in subsequent recursion).

It is important to note that the pointer pre is defined to hold the last accessed pointer, and should be defined as a reference because it needs to be changed in real time during a function call.

Traversing a Binary Thread Tree

//t points to the head node, the left chain lchild of the head node points to the root node, and the right chain rchild of the head node points to the last node traversed in middle order.
//Intermediate traversal of a binary threaded tree represents a binary tree t
int InOrderThraverse_Thr(BiTree t)
{
    BiTree p;
    p = t->lchild;                               //p points to the root node
    while(p != t)                               //Empty tree or end of traversal p == t
    {
        while(p->ltag == 0)                       //Loop to the first node of the sequence when ltag = 0
        {
            p = p->lchild;
        }
        printf("%c ", p->data);                      //Display node data, which can be changed to other node operations
        while(p->rtag == 1 && p->rchild != t)
        {
            p = p->rchild;
            printf("%c ", p->data);
        }
 
        p = p->rchild;                         //p goes into its right subtree
    }
 
    return OK;
}

Insertion Operation in Thread Tree

1. Insert the node referred to by q before the node referred to by p in the middle order

//Insert the node referred to by q before the node referred to by p in the middle order
template <class T>
void left_insert(node<T>* p, node<T>* q, node<T>** p_head) {//Incoming q only data
    node<T>* r;
    if (p->ltag == 1 || p->lchild == NULL) {//p No left child
        q->lchild = p->lchild; 
        q->ltag = p->ltag;
        q->rchild = p;       //Succession of p to q
        q->rtag = 1; 
        p->lchild = q;        //q as p's left child
        p->ltag = 0;
        if (q->lchild == NULL) *p_head = q;
    }
    else {       //Otherwise, q is the right child who originally pioneered p
        r = pred(p);//Find the pioneer of p
        q->rchild = r->rchild;     q->rtag = r->rtag;
        q->lchild = r;     q->ltag = 1;     
        r->rchild = q;     r->rtag = 0;
    }
}

//Finding a Precursor to Node t in an Ordered Thread Tree
template <class T>
node<T>* pred(node<T>* t) {
    if (t->ltag == 1 || t->lchild == NULL) return t->lchild;
    t = t->lchild;
    while (t->rtag == 0) t = t->rchild;  //T->rchild == NULL will not appear
    return t;
}

2. Insert the node referred to by q after the node referred to by p in the middle order

//Insert the node referred to by q after the node referred to by p in the middle order
template <class T>
void right_insert(node<T>* p, node<T>* q) {//p has no right child q is the right child of p
    node<T>* r;
    if (p->rtag == 1 || p->rchild == NULL)
    {
        q->rchild = p->rchild;     q->rtag = p->rtag;     
        q->lchild = p;             q->ltag = 1;    
        p->rchild = q;             p->rtag = 0;
    }    
    else {     //Otherwise q is the left child that succeeds p
        r = succ(p);    //Find the backend of p
        q->lchild = r->lchild;     q->ltag = r->ltag;
        q->rchild = r;    q->rtag = 1;    r->lchild = q;     r->ltag = 0;
    }
}

Delete Operation on Thread Tree

After deletion is required, the precursor-successor relationships of the remaining nodes remain unchanged

Take r, the right child of deleting t, as an example, in four cases:

(1) R is a leaf node: r's successor s, if present, s left child is not empty and need not be adjusted

(2) The left child of R is empty: to have the first node p in the right subtree of R (originally preceded by r) remember the precursor of R (in fact, t)  

  (3) r's right child is empty: let r's left subtree middle order last node p remember r's succession

(4) r has both left and right subtrees: the right subtree of R is connected to the last node P in the left subtree of R (as the right subtree of p), and the left child of the first node q in the right subtree of R points back to p

  (The specific code will be supplemented later.)

Tags: data structure

Posted on Mon, 22 Nov 2021 14:29:39 -0500 by edawson003