09--The application of binary tree Threaded binary tree

In the topic of data structure and algorithm, the related knowledge of linked list and binary tree and the implementation of sequential storage and chain storage of binary tree have been introduced:

1. Background

image.png

  • 1. The above figure is a binary tree structure. In the left and right subtrees of other nodes except node ABCD, 至少there is a pointer to ;
  • 2. When designing the structure of the nodule to save data, the leftChildsum rightChildpointer will be designed to point to the 左子树sum 右子树;
  • leftChild3. If the sum rightChildpointer of the node points to NULL, the applied pointer is taken 浪费;
  • 4. In order for 合理的利用the leftChild and rightChild pointers to point to one when they do not point , we can point them to OR based on 有意义the preorder, 中序, and postorder traversal logic of the binary tree .前驱后续

As shown below:

image.png

  • 1. 中序遍历The sequence obtained after use in the above figure is: H->D->I->B->J->E->A->F->C->G;
  • 2. Judging from the results obtained by the in-order traversal above, we can regard 中序遍历the obtained results 结果as one 链表traversal result;
  • 3. Compare the left pointer leftChild and the right pointer rightChild to 前驱pointers and 后续pointers in a doubly linked list;
  • 4. So we get a "H->D->I->B->J->E->A->F->C->G" 双向链表;
  • 5. In this way, we can fully use the wasted leftChildand rightChildpointer space 利用.

Through the above analysis, we solved the following two problems:

  • 1. The problem of wasting space;
  • 2. Under in-order traversal, quickly obtain the predecessor and subsequent problems of the node.

Thinking: How do we distinguish whether the leftChild(rightChild) of a node points to the left child (right child) or the predecessor (successor)?

image.png

  • 1. The node E in the figure, its leftChild points to the left child J, and its rightChild points to the subsequent node A;
  • 2. Node I, its leftChild points to the predecessor D, and its rightChild points to the subsequent B;
  • 3. Node B, its leftChild points to the left child D, and its rightChild points to the right child E;

Second, the design of clued binary tree nodes

We need to design nodes reasonably, and design a flag for leftChild and rightChild to record whether they point to the left and right children, or the predecessor and subsequent nodes.

image.png

  • ltag1. Add and use in the node rtagto mark lchild(rchild)the point to be 左孩子(右孩子)or 前驱(后续);

3. Code Implementation

1. Prepare

1. Define some states and data types

#define OK 1

#define ERROR 0

#define TRUE 1

#define FALSE 0

#define MAXSIZE 100 //存储空间初始分配量

//Status是函数的类型,其值是函数结果状态代码,如OK等
typedef int Status;

typedef char CElemType;

//Link==0表示指向左右孩子指针
//Thread==1表示指向前驱或后继的线索
typedef enum {Link,Thread} PointerTag;
复制代码

2. Create a character array for building a binary tree

//字符型以空格符为空 
CElemType Nil='#';

int indexs = 1;

typedef char String[24]; //0号单元存放串的长度

String str;

Status StrAssign(String T,char *chars)
{
    int i;
    
    if(strlen(chars)>MAXSIZE)
        return ERROR;
    else
    {
        T[0]=strlen(chars);

        for(i=1;i<=T[0];i++)
            T[i]=*(chars+i-1);
        return OK;
    }
}
复制代码

2. Node implementation

//线索二叉树存储结点结构
typedef struct BiThrNode{

    //数据
    CElemType data;

    //左右孩子指针
    struct BiThrNode *lchild,*rchild;

    //左右标记
    PointerTag LTag;
    PointerTag RTag;
    
}BiThrNode,*BiThrTree;
复制代码

3. Construct the binary tree

Status CreateBiThrTree(BiThrTree *T){
    CElemType h;
    
    //获取字符
    h = str[indexs++];

    if (h == Nil) {
        *T = NULL;
    }else{
        *T = (BiThrTree)malloc(sizeof(BiThrNode));

        if (!*T) {
            exit(OVERFLOW);
        }
        //生成根结点(前序)
        (*T)->data = h;

        //递归构造左子树
        CreateBiThrTree(&(*T)->lchild);

        //存在左孩子->将标记LTag设置为Link
        if ((*T)->lchild) (*T)->LTag = Link;

        //递归构造右子树
        CreateBiThrTree(&(*T)->rchild);

        //存在右孩子->将标记RTag设置为Link
        if ((*T)->rchild) (*T)->RTag = Link;
    }

    return OK;
}
复制代码
  • 1. strIt is StrAssignconstructed by a function 字符数组, and the 第0position of the array stores the array 长度;
  • 2. The initial value of indexes is 1, with recursion 依次递增;
  • 3. 前序The logic used is first created 双亲结点, then constructed 左子树, and finally constructed 右子树;
  • 4. After the construction 左子树(右子树), judge whether the current node exists 左孩子(右孩子). If there is 左孩子(右孩子), the current node's ltag(rtag)mark is Link, which means it lchild(rchild)points to 左孩子(右孩子).

4. In-order traversal of the binary tree T, the in-order threading

BiThrTree pre; //全局变量,始终指向刚刚访问过的结点

//中序遍历进行中序线索化
void InThreading(BiThrTree p){

    if (p) {
        //递归左子树线索化
        InThreading(p->lchild);
        
        //当前结点无左孩子
        if (!p->lchild) {
            //标记前驱
            p->LTag = Thread;

            //指向前驱
            p->lchild  = pre;
        }else
        {
            p->LTag = Link;
        }
        
        //前面刚访问过的结点(pre)没有右孩子
        if (!pre->rchild) {
            //标记后继
            pre->RTag = Thread;

            //pre结点的rchild指针指向后继(当前结点p)
            pre->rchild = p;
        }else
        {
            pre->RTag = Link;
        }

        //记录当前结点,方便下一次操作
        pre = p;

        //递归右子树线索化
        InThreading(p->rchild);
    }
}
复制代码
  • 1. First recursively 左子树carry out clues;
  • 2. Operate on the current node p, if the current node has 左孩子, then mark ltag as Threadand will lchildpoint to the node of the last operation pre; otherwise, ltag is marked as Link;
  • 3. Operate on the node of the last preoperation, if there 右孩子is pre, mark rtag as Thread, and rchildpoint to pre 后续p; otherwise, mark rtag as Link;
  • 4. 记录The current operation is 结点convenient for the next operation;
  • 5. Recursive 右子树threading.

The final result obtained through the above operation is as follows:

image.png

Through the above threading operation on the in-order traversal of the binary tree, we get an equivalent 双向链表structure, so we add a head node to it. The advantages of adding a head node are: 双向遍历, that is, you can start from the first node and follow the subsequent traversal; you can also start from the last node and follow the predecessor.

image.png

  • 1. Point the head node to the lchildbinary tree 根结点;
  • rchild2. Traverse the pointer of the head node in order 最后一个结点;
  • 3. Point to 第一个结点the in-order traversal ;lchild头结点
  • 4. Point to 最后一个结点the in-order traversal .rchild头结点

Threaded binary tree code implementation for adding head nodes

Status InOrderThreading(BiThrTree *Thrt , BiThrTree T){
    //创建头结点
    *Thrt=(BiThrTree)malloc(sizeof(BiThrNode));
    
    if (! *Thrt) {
        exit(OVERFLOW);
    }
    //头结点的ltag指向Link,rtag指向Thread
    (*Thrt)->LTag = Link;
    (*Thrt)->RTag = Thread;

    //rchild指向后续,暂时指向自己
    (*Thrt)->rchild = (*Thrt);
    
    //若二叉树空
    if (!T) {
        //lchild指向左孩子,即自己
        (*Thrt)->lchild = *Thrt;
    }else{
        //二叉树不为空,lchild指向二叉树的根结点
        (*Thrt)->lchild = T;
        //记录头结点,方便后面的线索化操作
        pre = (*Thrt);

        //中序遍历进行线索化
        InThreading(T);

        //经过中序遍历的线索化后pre最终指向了最后一个结点,pre的rchild指向头结点
        pre->rchild = *Thrt;

        //最后一个结点线索化
        pre->RTag = Thread;
        
        //头结点的rchild指向pre
        (*Thrt)->rchild = pre;
    }
    return OK;
}
复制代码

5. Inorder Traversal of Threaded Binary Trees


Status visit(CElemType e)
{
    printf("%c ",e);
    return OK;
}

Status InOrderTraverse_Thr(BiThrTree T){

    BiThrTree p;

    p = T->lchild; //p指向根结点,从根结点开始遍历

    //空树或遍历结束时,p == T
    while(p!=T)
    { 
        //从当前子树的根结点一路找到最左边的结点,即中序遍历的第一个结点
        while(p->LTag == Link) {
            p = p->lchild;
        }
        
        //打印当前结点
        if(!visit(p->data)) { 
             return ERROR;
        }
        //从当前结点一路找到后续结点,即中序遍历的最后一个结点
        while(p->RTag == Thread && p->rchild != T)
        {
            p = p->rchild;
            visit(p->data);
        }
        //遍历下一个子树
        p = p->rchild;
    }
    return OK;
}
复制代码

6. Debug

    BiThrTree H,T;
    
    StrAssign(str,"ABDH##I##EJ###CF##G##");
    
    CreateBiThrTree(&T); //按前序产生二叉树

    InOrderThreading(&H,T); //中序遍历,并中序线索化二叉树

    InOrderTraverse_Thr(H);
复制代码

4. Summary

Binary tree threading is to solve two problems:

  • 1. The 空间浪费problem;
  • 快速获取2. The problem of nodes under in-order traversal 前驱和后续.

Guess you like

Origin juejin.im/post/7078314308949508110