13、線索二叉樹

颜欢兮發表於2024-09-29

1、線索化二叉樹的定義和初始化

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

#define ElemType char

typedef struct BinTreeNode{
    ElemType data;
    struct BinTreeNode* leftChild;
    struct BinTreeNode* rightChild;
    int lTage;//左標記[0:左孩子節點 1:前驅節點] 
    int rTage;//右標記[0:右孩子節點 1:後繼節點] 
}BinTreeNode;

typedef struct BinTree{
    BinTreeNode* root;
    ElemType refvalue;//停止標誌 
}BinTree; 


//初始化
void initBinTree(BinTree* bt,ElemType ref){
    bt->root = NULL;
    bt->refvalue = ref;
} 

2、二叉樹的建立和線索化

//建立節點
BinTreeNode* malloc_node(ElemType data){
    BinTreeNode* t = (BinTreeNode*) malloc(sizeof(BinTreeNode));
    assert(t != NULL);
    t->data = data;
    t->leftChild = NULL;
    t->rightChild = NULL;
    t->lTage = t->rTage = 0;
    return t;
} 

//建立二叉樹
void createBinTree(BinTree* bt,BinTreeNode** node,char** str){
    if(**str == bt->refvalue){
        (*node) = NULL;
        ++(*str);
    } else {
        (*node) = malloc_node(**str);
        ++(*str);
        createBinTree(bt,&(*node)->leftChild,str);
        createBinTree(bt,&(*node)->rightChild,str);
    }
} 

//二叉樹中序線索化
void BinTreeNodeInThread(BinTreeNode** t,BinTreeNode** pre){
    if(*t == NULL)
        return;
    else {
        //遍歷當前節點的所有左子樹
        BinTreeNodeInThread(&(*t)->leftChild,pre);
        
        //執行到此處說明該節點 的左子樹已全部遍歷完
        //處理該節點 將左節點進行線索化
        if((*t)->leftChild == NULL){
            (*t)->lTage = 1;
            (*t)->leftChild = *pre;
        }
        //處理該節點的前驅節點,將該節點的前驅節點線索化[前驅節點的後繼指向t] 
        if((*pre) != NULL && (*pre)->rightChild == NULL){
            (*pre)->rTage = 1;
            (*pre)->rightChild = *t;
        }
        //更新前驅節點
        *pre = *t;

        //遍歷當前節點的所有右子樹
        BinTreeNodeInThread(&(*t)->rightChild,pre);
    }
} 

void BinTreeInThread(BinTree* bt){
    //記錄前驅節點 
    BinTreeNode* pre = NULL;
    BinTreeNodeInThread(&bt->root,&pre); 
    //處理最後一個節點
    if(pre != NULL){
        pre->rightChild = NULL;
        pre->rTage = 1; 
    }

} 

3、線索化二叉樹的其他方法

//求中序遍歷第一個節點
BinTreeNode* First(BinTreeNode* node){
    
    BinTreeNode* p = node;
    //遍歷左孩子,當遍歷到左標記為1說明是第一個節點 
    //由於已經線索化了,所有節點的左右孩子都不為NULL 
    while(p != NULL && p->lTage == 0)
        p = p->leftChild;
    return p;
} 

//求中序遍歷最後一個節點
BinTreeNode* Last(BinTreeNode* node){
        
    BinTreeNode* p = node;
    while(p != NULL && p->rTage == 0){
        p = p->rightChild;
    }
        
    return p;
} 

//求cur節點的後繼
BinTreeNode* Next(BinTreeNode* root,BinTreeNode* cur){
    if(root == NULL || cur == NULL)
        return NULL;
    if(cur->rTage == 1)
        return cur->rightChild;
    //由於是中序遍歷,說明cur左子樹的節點都遍歷完了,
    //所以cur的後繼節點為它的右子樹遍歷的第一個節點
    return First(cur->rightChild); 
} 

//求cur節點的前驅
BinTreeNode* Prio(BinTreeNode* root,BinTreeNode* cur){
    if(root == NULL || cur == NULL)
        return NULL;
    if(cur->lTage == 1)
        return cur->leftChild;
    //由於是中序遍歷,cur的前驅為它的左子樹遍歷的最後一個節點 
    return Last(cur->leftChild); 
} 

//中序遍歷[由於二叉樹已經線索化,不能再用之前的遍歷方法,
//線索化將二叉樹 由非線性變為線性關係] 
void InOrder(BinTreeNode* root){
    BinTreeNode* p = root;
    for(p = First(p);p != NULL;p = Next(root,p)){
        printf("%c ",p->data);
    }
    printf("\n");
} 

BinTreeNode* search(BinTreeNode* root,ElemType key){
    if(root == NULL)
        return NULL;
    if(root->data == key)
        return root;
    BinTreeNode* p = root;
    for(p = First(p);p != NULL;p = Next(root,p)){
        if(p->data == key)
            return p;
    }    
    return NULL;
}

BinTreeNode* Parent(BinTreeNode* root, BinTreeNode* cur){
     if (root == NULL || cur == NULL || root == cur) {
        return NULL;
    }
    
    BinTreeNode* p;
    //cur存線上索 注意:if中一定要判斷p是否為空 
    if(cur->lTage == 1){
        p = cur->leftChild;
        //由於是中序遍歷,cur的左孩子為前驅節點,既然是前驅說明cur一定在指向的右子樹這邊 
        if(p != NULL && p->rightChild == cur)
            return p;
    }
    if(cur->rTage == 1){
        p = cur->rightChild;
        if(p != NULL  && p->leftChild == cur)
            return p;
    }
     
    //cur不存線上索 ,說明cur存在左子樹,那麼左子樹在cur前面
    //找到cur左子樹要顯示的第一個節點,該節點一定沒有左子樹
    //故該節點的左孩子節點會指向它的前驅,這個前驅有可能就是cur的父節點 
    p = First(cur->leftChild);
    p = p->leftChild;
    if(p != NULL && p->rightChild == cur)
        return p;
    
    //如果上訴都沒找到,那就找cur節點的右孩子最後中序遍歷的最後一個節點
    //這個節點的後繼就是cur的父節點,因為這個節點在cur右邊,說明cur已經訪問了
    //cur的中序遍歷最後一個右節點也訪問,根據左根右說明下一個就是cur的父節點
    p = Last(cur->leftChild);
    if (p != NULL && p->rightChild == cur) {
        return p;
    }
    return NULL; 
} 
int main(){
    BinTree myTree;
    initBinTree(&myTree,'#');
    char* str = "ABC##DE##F##G#H##";
    createBinTree(&myTree,&myTree.root,&str);
    BinTreeInThread(&myTree);
//    BinTreeNode* node = Last(myTree.root);
//    printf("%c",node->data);
    InOrder(myTree.root);
    BinTreeNode* p = search(myTree.root,'C');
//    BinTreeNode* next = Next(myTree.root,p);
    BinTreeNode* parent = Parent(myTree.root,p);
    printf("%c",parent->data);
    
    return 0;
}

相關文章