Data Structure - Trees and Binary Trees (Part III)

Posted by sara_kovai on Sat, 18 Dec 2021 18:55:54 +0100

Data Structure - Trees and Binary Trees (Chapter VII). If there are any errors, please correct them.

Constructing Binary Trees from Traversal Sequences

Yes can can Of three species feeling condition { Front order + in order Throughout calendar order column after order + in order Throughout calendar order column layer order + in order Throughout calendar order column Three possible scenarios\begin{cases}Pre+Medium Traversal Sequence\Post+Medium Traversal Sequence\Sequence+Medium Traversal Sequence\\end{cases} There are three possible scenarios. Pre-order+Median traversal sequence Post-order+Median traversal sequence sequence sequence+Median traversal sequence sequence

  • A binary tree cannot be uniquely identified if only one of the sequence of front/middle/back/sequence traversal of a binary tree is given. In addition, the combination of the precedent, the postorder and the sequence sequence cannot uniquely determine a binary tree.

Sequence + Sequence

Postordinal + Median Sequences

Sequence sequence + Intermediate sequence

Threaded Binary Tree

  • Threaded binary trees are introduced to speed up the search of node precursors and successors, because traditional binary chain lists can only represent a parent-child relationship, and can not directly get the precursors or successors of nodes in traversal.
    Find the intermediate precursor of node p using the original method:
typedef struct BiTNode
{
    ElemType data;
    struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;

BiTNode* p, * pre = NULL, * final = NULL;

void FindPre(BiTNode *t)
{
    if (t != NULL)
    {
        FindPre(t->lchild); //Recursively traverse left subtree
        visit(t); //Access Root Node
        FindPre(t->rchild); //Recursively traverse right subtree
    }
}

void visit(BiTNode* q)
{
    if (q == p) final = pre; //The current visiting node happens to be node p, finding a precursor to p
    else pre = q; //pre points to the currently accessed node
}
  • Provision: If there is no left subtree, make lchild point to its precursor node; If there is no right subtree, make rchild point to its successor nodes. Two additional marker fields are needed to identify whether the pointer field is to the left (right) child or to the front (next).

    Among them, the meaning of the flag field is as follows:

l t a g { 0 ,      l c h i l d field finger show junction spot Of Left Child son 1 ,      l c h i l d field finger show junction spot Of Front drive ltag\begin{cases} 0, \;\; The lchild field indicates the left child of the node\1, \;\; The lchild field indicates the precursor\\\end{cases} of the node ltag{0, lchild field indicates left child 1 of the node, lchild field indicates the precursor of the node
r t a g { 0 ,      r c h i l d field finger show junction spot Of right Child son 1 ,      r c h i l d field finger show junction spot Of after Following rtag\begin{cases} 0, \;\; The rchild field indicates the right child of the node\1, \;\; The rchild field indicates the successor\\\end{cases} of the node rtag{0, the rchild field indicates the right child of the node 1, and the rchild field indicates the successor of the node

Description of storage structure of threaded binary tree

typedef struct ThreadNode
{
	ElemType data;
	struct ThreadNode* lchild,*rchild;
	int ltag,rtag;
}ThreadNode,*ThreadTree; 
  • The list of binary chains made up of this node structure is called the list of threaded chains as the storage structure of binary trees, and the pointers to the precursors and successors of nodes are called threads. A binary tree with threads is called a threaded binary tree.

Intermediate-order threaded binary tree

Code implementation (headless node)

ThreadNode* pre = NULL;

void CreateInThread(ThreadNode* t)
{
    if(t != NULL) InThread(t); //Non-empty binary trees can be threaded, medium-order threaded binary trees
    if (pre->rchild == NULL) pre->rtag = 1; //Processing the last traversed node
}

void InThread(ThreadNode* t) //Intermediate traversal
{
    if (t != NULL) //Traversing when not an empty tree
    {
        InThread(t->lchild); //Traversing left subtree in middle order
        visit(t); //Access Root Node
        InThread(t->rchild); //Medium-order traversal of right subtree
    }
}

void visit(ThreadNode* q)
{
    if (q->lchild == NULL) //Left subtree is empty, building precursor clues
    {
        q->lchild = pre;
        q->ltag = 1;
    }
    if (pre != NULL && pre->rchild == NULL)
    {
        pre->rchild = q; //Establishing a successor thread for a precursor node
        q->rtag = 1;
    }
    pre = q;
}

Program analysis

Code implementation (with header node)

  • For convenience, a header node can also be added to the list of binary tree threads, so that the pointer of its lchild field points to the root node of the binary tree, and the pointer of its rchild field points to the last node accessed during the middle traversal. Make the lchild domain pointer of the first node in the sequence of binary trees and the rchild domain pointer of the last node point to the head node. This is like building a two-way threaded list of binary trees to facilitate traversing threaded binary trees from back to front or from back to front.
ThreadNode* pre = NULL; //global variable

void InThread(ThreadNode* &p) //Intermediate threading of binary tree t
{
    if (p != NULL)
    {
        InThread(p->lchild); //Left subtree threading
        if (p->lchild == NULL) //Left child does not exist, threading forward nodes
        {
            p->lchild = pre; //Establishing a precursor node thread for the current node
            p->ltag = 1;
        }
        else p->ltag = 0; //The left subtree of the t-node is threaded
        if (pre->rchild == NULL) //Threading the succeeding nodes of pre p
        {
            pre->rchild = t; //Establishing a successor node thread for a precursor node
            pre->rtag = 1;
        }
        else pre->rtag = 0;
        pre = p;
        InThread(t->rchild); //Right subtree threading
    }
}

ThreadNode* CreatThread(ThreadNode *&t)
{
    ThreadNode* t, * root;
    root = (ThreadNode*)malloc(sizeof(ThreadNode)); //Create Head Node
    root->ltag = 0; //The lchild of the head node points to the left child (that is, to the root node)
    root->rtag = 1; //The rchild of the header node points to the thread (that is, to the last node visited during intermediate traversal)
    root->rchild = t; //The rchild of the first header node points to the root node
    if (t == NULL) root->lchild = pre; //When an empty binary tree is empty, the lchild of the head node points to itself
    else //Binary tree is not empty
    {
        root->lchild = t; //The lchild of the header node points to the root node
        pre = root; //pre is the precursor to node t for threading
        ThreadTree(t); //Intermediate traversal threaded binary tree
        pre->rchild = root; //Finally, add a thread pointing to the head node
        pre->rtag = 1;
        t->rchild = pre; //Head Node Right Threaded
    }
    return root;
}

Program analysis

  • The right child pointer of the last node traversed in the middle order must be empty, so instead of determining whether the rchild is NULL, you can directly point the rchild to NULL and make rtag=1.

Sequential Threaded Binary Tree

Code implementation (headless node)

ThreadNode* pre = NULL;

void CreatePreThread(ThreadNode* t)
{
    if(t != NULL) PreThread(t); //Non-empty binary trees can be threaded, medium-order threaded binary trees
    pre->rchild == NULL;
    pre->rtag = 1; //Processing the last traversed node
}

void PreThread(ThreadNode* t) //Preorder traversal
{
    if (t != NULL) //Traversing when not an empty tree
    {
        visit(t); //Access Root Node
        if (t->ltag == 0) PreThread(t->lchild); //lchild is not a precursor thread, traversing left subtree in preceding order
        PreThread(t->rchild); //Pre-order traversal of right subtree
    }
}

void visit(ThreadNode* q)
{
    if (q->lchild == NULL) //Left subtree is empty, building precursor clues
    {
        q->lchild = pre;
        q->ltag = 1;
    }
    if (pre != NULL && pre->rchild == NULL)
    {
        pre->rchild = q; //Establishing a successor thread for a precursor node
        q->rtag = 1;
    }
    pre = q;
}

Program analysis

  • ! Note: Dead cycle in sequential threading.
  • Assuming that q points to the third node D at this point, the precursor node prep of q should point to the second node B.
  • In the order in which the code is executed:
if (q->lchild == NULL) //Left subtree is empty, building precursor clues
    {
        q->lchild = pre;
        q->ltag = 1;
    }
  • q.lchild==NULL, let q.lchild point to pre, q.ltag=1.
  • The code continues to execute:
if (pre != NULL && pre->rchild == NULL)
    {
        pre->rchild = q; //Establishing a successor thread for a precursor node
        q->rtag = 1;
    }
  • At this point although pre!= NULL, but pre. Rchild!= NULL, so skip this part of the code and execute pre = q;.
  • Q and prepoint to the third node D at the same time, since the order of sequential access is: around the root. At this point, D is the root node, it should continue to execute the left subtree of D after execution, and D's left child is NULL, which will make q.lchild point to its precursor node, that is, D points back to B again. So the program repeats this over and over again.
  • To avoid this endless loop, you can decide if q.lchild is 0 or 1 after the root node is executed, and if it is 1, it represents the left child and threaded, without having to deal with it at this time. Only if q.child is 0, the left child of the root node is not threaded and can proceed.

Code implementation (with header node)

void PreThread(ThreadNode*& p)
{
    if (p != NULL)
    {
        if (p->lchild == NULL)
        {
            p->lchild = pre;
            p->ltag = 1;
        }
        else p->ltag = 0;
        if (pre->rchild == NULL)
        {
            pre->rchild = p;
        }
        if(p->ltag==0) PreThread(p->lchild);
        PreThread(p->rchild);
    }
}

ThreadNode * CreatThread(ThreadNode *&t)
{
    ThreadNode* t, * root;
    root = (ThreadNode*)malloc(sizeof(ThreadNode)); //Create Head Node
    root->ltag = 0; //The lchild of the head node points to the left child (that is, to the root node)
    root->rtag = 1; //The rchild of the header node points to the thread (that is, to the last node visited during the preamble traversal)
    root->rchild = t; //The rchild of the first header node points to the root node
    if (t == NULL) root->lchild = pre; //When an empty binary tree is empty, the lchild of the head node points to itself
    else //Binary tree is not empty
    {
        root->lchild = t; //The lchild of the header node points to the root node
        pre = root; //pre is the precursor to node t for threading
        ThreadTree(t); //Preorder Traversal Threaded Binary Tree
        pre->rchild = root; //Finally, add a thread pointing to the head node
        pre->rtag = 1;
        t->rchild = pre; //Head Node Right Threaded
    }
    return root;
}

Program analysis

Postordinal Threaded Binary Tree

Code implementation (headless node)

void CreatePostThread(ThreadNode* t)
{
    if(t != NULL) PostThread(t); //A non-empty binary tree can be threaded, followed by a threaded binary tree
    if (pre->rchild == NULL) pre->rtag = 1; //Processing the last traversed node
}

void PostThread(ThreadNode* t) //Preorder traversal
{
    if (t != NULL) //Traversing when not an empty tree
    {
        PostThread(t->lchild); //Post-order traversal of left subtree
        PostThread(t->rchild); //Post-order traversal of right subtree
        visit(t); //Access Root Node
    }
}

void visit(ThreadNode* q)
{
    if (q->lchild == NULL) //Left subtree is empty, building precursor clues
    {
        q->lchild = pre;
        q->ltag = 1;
    }
    if (pre != NULL && pre->rchild == NULL)
    {
        pre->rchild = q; //Establishing a successor thread for a precursor node
        q->rtag = 1;
    }
    pre = q;
}

Program analysis

  • Special attention should be paid to the dead-loop problem in the ordered-threaded binary tree, but not in the ordered and postordered-threaded binary trees.
  • The code of a post-ordered threaded binary tree is basically the same as that of an ordered threaded binary tree, except that the middle-ordered traversal is about the root, and the second-ordered traversal is about the left-right root. In addition, in the sequential threaded binary tree, the last node needs to determine if its right child is NULL and if it is NULL, change ltag to 1. The ordered-threaded binary tree can be changed to 1 without judgment.

Code implementation (with header node)

void PostThread(ThreadNode*& p)
{
    PreThread(p->lchild);
    PreThread(p->rchild);
    if (p != NULL)
    {
        if (p->lchild == NULL)
        {
            p->lchild = pre;
            p->ltag = 1;
        }
        else p->ltag = 0;
        if (pre->rchild == NULL)
        {
            pre->rchild = p;
        }
    }
}

ThreadNode * CreatThread(ThreadNode *&t)
{
    ThreadNode* t, * root;
    root = (ThreadNode*)malloc(sizeof(ThreadNode)); //Create Head Node
    root->ltag = 0; //The lchild of the head node points to the left child (that is, to the root node)
    root->rtag = 1; //The rchild of the header node points to the thread (that is, to the last node visited during postsequential traversal)
    root->rchild = t; //The rchild of the first header node points to the root node
    if (t == NULL) root->lchild = pre; //When an empty binary tree is empty, the lchild of the head node points to itself
    else //Binary tree is not empty
    {
        root->lchild = t; //The lchild of the header node points to the root node
        pre = root; //pre is the precursor to node t for threading
        ThreadTree(t); //Post-order Traversal Threaded Binary Tree
        pre->rchild = root; //Finally, add a thread pointing to the head node
        pre->rtag = 1;
        t->rchild = pre; //Head Node Right Threaded
    }
    return root;
}

Program analysis

Finding a Precursor & Successor in a Cable Binary Tree

Finding a Precursor in an Ordered-Threaded Binary Tree

stay in order Line Cable two fork tree in look for reach finger set junction spot ∗ p Of in order Front drive p r e { ① if p − > l t a g = = 1 , be p r e = p − > l c h i l d ② if p − > l t a g = = 0 , be say bright p have to Yes Left Child son Find the intermediate precursor pre\begin{cases} of the specified node *p in the ordered threaded binary tree. If p->ltag==1, then pre = p->lchild \2 If p->ltag==0, P must have a left child \\end{cases} If p>ltag==1, then pre=p>lchild2. If p>ltag==0, then p must have a left child.

code implementation

ThreadNode* LastNode(ThreadNode* p) //Find the last node in the subtree rooted in P that was traversed in middle order
{
    while (p->ltag == 0) p = p->rchild; //Loop to find the rightmost lower node (not necessarily a leaf node)
    return p;
}

ThreadNode* PreNode(ThreadNode* p) //Finding the precursor node of node p in the ordered threaded binary tree
{
    if (p->ltag == 1) return p->lchild; //ltag=1 returns directly to the lead
    else return LastNode(p); //Lowest Right Node in Left Subtree
}

void RevInOrder(ThreadNode* t) //Reverse middle-order traversal of ordered threaded binary trees
{
    for (ThreadNode* p = LastNode(t); p != NULL; p = PreNode(p))
        printf("%c", p->data);
}

Find Successors in an Ordered-Threaded Binary Tree

stay in order Line Cable two fork tree in look for reach finger set junction spot ∗ p Of in order after Following p r e { ① if p − > r t a g = = 1 , be p r e = p − > r c h i l d ② if p − > r t a g = = 0 , be say bright p have to Yes right Child son Find the intermediate sequence successor pre\begin{cases} of the specified node *p in the ordered threaded binary tree. If p->rtag==1, then pre = p->rchild \2 If p->rtag==0, P must have a right child\\end{cases} Finding the specified node P in the ordered threaded binary tree is followed by pre{1 if p>rtag==1 then pre=p>rchild 2 if p>rtag==0 then p must have a right child.

code implementation

ThreadNode* LastNode(ThreadNode* p) //Find the first node to be traversed in middle order in a P-rooted subtree
{
    while (p->ltag == 0) p = p->lchild; //Loop to find the leftmost lower node (not necessarily a leaf node)
    return p;
}

ThreadNode* NextNode(ThreadNode* p) //Finding successor nodes to node p in ordered threaded binary tree
{
    if (p->rtag == 1) return p->rchild; //ltag=1 returns directly to the next thread
    else return LastNode(p); //Lowest Left Node in Right Subtree
}

void RevInOrder(ThreadNode* t) //Reverse middle-order traversal of ordered threaded binary trees
{
    for (ThreadNode* p = LastNode(t); p != NULL; p = NextNode(p))
        printf("%c", p->data);
}

Find a pioneer in a sequential threaded binary tree

  • ! It is not possible to find a precursor in a binary tree with a precursor thread. Because in a precedent traversal, the nodes in the left and right subtrees may only be successors of the roots, not precursors.
  • There are two solutions:
    1. Traverse from scratch using soil method to define a *prepoint to the precursor node of the current node and *final point to the final precursor node to be found.
    (2) Add a pointer field parent to the list of 3-fork chains.

Analysis

if send use 3 fork chain surface , be Yes four species can can Of feeling condition { ① p junction spot only Yes Left son tree , no Yes right son tree ② p junction spot only Yes right son tree , no Yes Left son tree ③ p junction spot as well as Yes Left son tree , also Yes right son tree ④ p junction spot by root section spot If you use a 3-fork chain table, there are four possible cases \begin{cases} 1 p node has only a left subtree, no right subtree\2 p node has only a right subtree, and no left subtree\3 p node has both a left subtree and a right subtree\4 p node is a root node\\end{cases} If a 3-fork chain table is used, there are four possible scenarios: (1) p-node has only left subtree, no right subtree, (2) p-node has only right subtree, no left subtree, (3) p-node has both left subtree and right subtree, (4) p-node is root.

Find successors in a sequential threaded binary tree

Yes can can Of two species feeling condition { ① p junction spot Yes right Child son , no Yes Left Child son ② p junction spot Yes right Child son , also Yes Left Child son There are two possible scenarios \begin{cases}1 p node has a right child, no left child\2 p node has a right child, and there are left children\\end{cases} There are two possible scenarios {1)p node has right child, no left child, and P node has right child and left child.

code implementation

ThreadNode* NextNode(ThreadNode* p) //Finding the successor nodes of node p in the sequential threaded binary tree
{
    if (p->rtag == 1) return p->rchild; //ltag=1 returns directly to the next thread
    else
    {
        if (p->ltag == 1) p->rchild; //If the p-node has no left child, the right child is its successor
        else return p->lchild; //p node has left child, left child is its successor node
    }
}

Finding a Precursor in a Postsequential Threaded Binary Tree

Yes can can Of two species feeling condition { ① p junction spot Yes Left Child son , no Yes right Child son ② p junction spot Yes Left Child son , also Yes right Child son There are two possible scenarios\begin{cases}1 p node has left children, no right children\2 p node has left children, and right children\\end{cases} There are two possible scenarios {1)p node has left children, no right children, and P node has left children and right children.

code implementation

ThreadNode* PreNode(ThreadNode* p) //Find the precursor node of node p in the sequential threaded binary tree
{
    if (p->ltag == 1) return p->lchild; //ltag=1 returns directly to the lead
    else
    {
        if (p->rtag == 1) p->rchild; //p node has a right child, then the right child is its successor
        else return p->lchild; //If the p-node has no right child, the left child is its successor
    }
}

Finding Successors in a Successor-Threaded Binary Tree

  • ! Successors in a sequential threaded binary tree cannot be found based on successor threads. Because in post-order traversal, nodes in left and right subtrees may only be precursors to the root, not successors.
  • There are two solutions:
    1. Traverse from scratch using soil method to define a *pre point to the successor node of the current node and *final point to the successor node that is ultimately to be found.
    (2) Add a pointer field parent to the list of 3-fork chains.

Analysis

if send use 3 fork chain surface , be Yes four species can can Of feeling condition { ① p junction spot Yes father section spot , And p by father section spot Of right Child son , p junction spot no Yes Left Child son ② p junction spot Yes father section spot , And p by father section spot Of Left Child son , p junction spot no Yes right Child son ③ p junction spot Yes father section spot , And father section spot Yes Left , right Child son ④ p junction spot by root section spot If you use a 3-fork chain list, there are four possible scenarios \begin{cases} 1 P node has a parent node and P is the right child of the parent node, P node has no left child\2 P node has a parent node, P node has no right child\3 P node has a parent node, and the parent node has left and right children\ p node is the root node\casesend} If a 3-fork chain table is used, there are four possible situations: (1) p node has a parent node and p is the right child of the parent node, p node has no left child; (2) p node has a parent node and p is the left child of the parent node, p node has no right child; (3) p node has a parent node and p node has no right child; (3) p node has a parent node and the parent node has left and (4) p node is the root node.

Topics: C Algorithm data structure