C language - clue binary tree (preorder, inorder, postorder - with code)

 1. What is a clue binary tree

Threaded Binary Tree (Threaded Binary Tree) is a special binary tree. By changing the null pointer to a thread (that is, the predecessor or successor pointer), the free pointer in the binary tree is used to achieve efficient traversal and search of the binary tree.

Threading is the process of scanning a binary tree in a traversal manner and adding a thread to each node.

  • The node definition of the thread binary tree contains two flag bits, respectively indicating whether its left and right pointers are threads.
  • If the left child node is empty, point its left child pointer to the immediate predecessor node of this node;
  • If the right child node is empty, set its right child pointer to the node's immediate successor.

In this way, after threading, the predecessor and successor of the node can be quickly located through the thread, which avoids unnecessary traversal and improves search efficiency.

The main benefit of the thread binary tree is that it can improve the traversal efficiency of the binary tree.

The traversal of conventional binary trees requires the use of data structures such as recursion or stacks, which is inefficient. The thread binary tree realizes the efficient traversal and search of the binary tree by changing the original empty pointer to the predecessor or successor thread.

At the same time, the thread binary tree can effectively save storage space, avoid storing additional null pointers in nodes, and improve memory utilization.

2. All codes (detailed notes inside)

2.0, file structure and code examples

It should be noted that because the pointer is passed by value, if you need to modify the value of the pointer variable (such as pNode) in the function, you must use a pointer to the pointer or a reference to the pointer, otherwise the modification will not take effect . In the code, since the function does not return a value, it is impossible to determine whether the function is executed successfully. If errors or unusual conditions occur, programs may behave unexpectedly or crash.

Binary tree built in code

 

2.1、main.cpp

#include "test.h"
int main() {

    char *str = "123##45##6##7#8##";     // 前序构造二叉树
    BThead binTreeHead;
    initBinTree(&binTreeHead,'#');

    createBinTreeNode(&binTreeHead,str);  // 前序构造二叉树

    ForEachBinTree(&binTreeHead,1);    // 遍历二叉树(1:前序,2:中序,3:后序)
    threadBinTree(&binTreeHead, 1);    // 线索二叉树(1:前序,2:中序,3:后序)


    printf("%p",&binTreeHead);
}

2.2、test.h

#pragma once

#include<stdio.h>
#include<malloc.h>
#include<assert.h>
#include <math.h>
#include <iostream>

//元素类型
#define ElemType char
#define MyType int


// 二叉树结点
typedef struct BinTreeNode{
    // 标志  0代表是孩子  1代表是前驱或后继
    MyType L_tag;
    MyType R_tag;

    // 节点
    struct BinTreeNode *L_child;
    struct BinTreeNode *R_child;

    // 值
    ElemType value;
}BTnode, *BT;

// 二叉树的头结点
typedef struct BinTreeHead{
    ElemType endFlag;
    BinTreeNode *BTNode;
}BThead;



void initBinTree(BinTreeHead *head, ElemType endFlag);  // 头节点初始化

void createBinTreeNode(BinTreeHead *head, char *str);  // 接收 头结点  一个字符串地址

void ForEachBinTree(BinTreeHead *head,int i); // 遍历二叉树(1:前序,2:中序,3:后序)

void threadBinTree(BinTreeHead *head, int i); // 线索二叉树(1:前序,2:中序,3:后序)

2.3、test.cpp


#include"test.h"
// 定义数据类型
struct BinTreeNode *pre = NULL;

// 申请结点
BinTreeNode* mallocNode(ElemType e){
    BinTreeNode *newNode = (BinTreeNode*)malloc(sizeof(BinTreeNode));
    assert(newNode != NULL);
    newNode->L_child = newNode->R_child = NULL;
    newNode->L_tag = newNode->R_tag = 0;  //线索化之前左右标记都初始为指针链
    newNode->value = e;
    return newNode;
}

// 头节点初始化
void initBinTree(BinTreeHead *head, ElemType endFlag){
    head->endFlag = endFlag;
    head->BTNode = NULL;

}

// 创建二叉树(前序)
void createBTreeNode(BinTreeHead *pHead, BinTreeNode **pNode, char **pString) {

//    printf("%d \n",**pString);
//    printf("%d \n",pHead->endFlag);
    // 如果终止:
    if(**pString == pHead->endFlag){
        *pNode = NULL;
        return;
    }

    // 创建新的根结点
    *pNode = mallocNode(**pString);

    // 创建左孩子
    (*pString)++;
    createBTreeNode(pHead, &((*pNode)->L_child), pString);
    // 创建右孩子
    (*pString)++;
    createBTreeNode(pHead, &((*pNode)->R_child), pString);

}

// 创建二叉树的头结点
void createBinTreeNode(BinTreeHead *head, char *str){
    createBTreeNode(head, &(head->BTNode), &str);
}

//---------------------------------------遍历二叉树
// 输出二叉树
void visit(BinTreeNode *pNode){

    printf("value: %c \n",pNode->value);

}

// 前序遍历二叉树
void preOrder(BinTreeNode *pNode){

    if(pNode == NULL){
        return;
    }

    visit(pNode);  // 输出结点value

    preOrder(pNode->L_child);
    preOrder(pNode->R_child);
}

// 中序遍历二叉树
void inOrder(BinTreeNode *pNode){

    if(pNode == NULL){
        return;
    }

    inOrder(pNode->L_child);
    visit(pNode);  // 输出结点value
    inOrder(pNode->R_child);
}
// 后序遍历二叉树
void afterOrder(BinTreeNode *pNode){

    if(pNode == NULL){
        return;
    }

    afterOrder(pNode->L_child);
    afterOrder(pNode->R_child);
    visit(pNode);  // 输出结点value
}

// 用头结点遍历二叉树
void ForEachBinTree(BinTreeHead *head,int i){
    // 如果是1,则前序遍历
    if(i==1){
        preOrder(head->BTNode);
        return;
    }
    // 如果是2,则中序遍历
    if(i==2){
        inOrder(head->BTNode);
        return;
    }
    // 如果是3,则后序遍历
    if(i==3){
        afterOrder(head->BTNode);
        return;
    }
    printf("请输入对应的数字!");
}
//-------------------------------------------------------------- 线索二叉树

// 线索化
void createThread(BinTreeNode **pNode){


    // 如果左孩子为空      (前驱)
    if((*pNode)->L_child == NULL && (*pNode)->L_tag == 0){
        (*pNode)->L_tag = 1;
        (*pNode)->L_child = pre;
    }

    // 如果pre的右孩子为空  (后继)
    if(pre->R_child == NULL && (*pNode)->R_tag == 0){
        pre->R_tag = 1;
        pre->R_child = *pNode;
    }

    // 回退之前 pre 赋值 当前结点
    pre = (*pNode);

}

// 前序
void preThreadBinTree(BinTreeNode **pNode){

    if(*pNode == NULL){
        return;
    }
    createThread(&(*pNode));  // 线索化
    if((*pNode)->L_tag == 0){    // 如果没有被线索化 !!! --------防止转圈 !!!
        preThreadBinTree(&((*pNode)->L_child));
    }
    preThreadBinTree(&((*pNode)->R_child));

}

// 中序
void inThreadBinTree(BinTreeNode **pNode){
    if(*pNode == NULL){
        return;
    }
    inThreadBinTree(&((*pNode)->L_child));
    createThread(&(*pNode));  // 输出结点value
    inThreadBinTree(&((*pNode)->R_child));

}

// 后序
void afterThreadBinTree(BinTreeNode **pNode){
    if(*pNode == NULL){
        return;
    }

    preThreadBinTree(&((*pNode)->L_child));
    preThreadBinTree(&((*pNode)->R_child));
    createThread(&(*pNode));  // 输出结点value
}


// 创建线索二叉树
void threadBinTree(BinTreeHead *head, int i){
    // pre初始化 - 让其指向根结点
    pre = head->BTNode;

    // 如果是1,则前序 创建线索二叉树
    if(i==1){
        preThreadBinTree(&(head->BTNode));
        return;
    }
    // 如果是2,则中序 创建线索二叉树
    if(i==2){
        inThreadBinTree(&(head->BTNode));
        return;
    }
    // 如果是3,则后序 创建线索二叉树
    if(i==3){
        afterThreadBinTree(&(head->BTNode));
        return;
    }
    printf("请输入对应的数字!");
}



3. Matters needing attention

When constructing a thread binary tree, the following points need to be paid attention to:

  1. During the threading process, it is necessary to ensure that the threaded nodes cannot be threaded again, otherwise an infinite loop will be formed.

  2. Since the threading operation will change the original pointer relationship, it needs to be restored after construction to ensure the integrity of the binary tree structure.

  3. If you use clues to traverse the binary tree, you need to consider how to use the predecessor and successor clues appropriately to avoid errors during the traversal process.

When constructing preorder, inorder, and postorder, the following points need to be paid attention to:

  1. Through preorder traversal and inorder traversal, a binary tree can be uniquely determined. Therefore, when constructing, it is necessary to ensure that the input preorder and inorder sequences are correct, and the elements in the sequence are not repeated.

  2. For in-order traversal and post-order traversal, a binary tree can also be uniquely determined. When constructing, it is necessary to satisfy that the input inorder and postorder sequences are correct, and the elements in the sequence are not repeated.

  3. In actual operation, you need to pay attention to the boundary conditions of recursion to avoid abnormal conditions such as infinite loops or overflows.

Guess you like

Origin blog.csdn.net/Pan_peter/article/details/130386551