12、二叉樹

颜欢兮發表於2024-09-29

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

//二叉樹節點定義 
typedef struct BinTreeNode{
    ElemType data;
    struct BinTreeNode* leftChild;
    struct BinTreeNode* rightChild;
}BinTreeNode;

//二叉樹定義 
typedef struct BinTree{
    BinTreeNode* root;
    ElemType refvalue; //結束標記 
}BinTree;

//初始化二叉樹,並規定結束標記為 #
void initBinTree(BinTree* tree,ElemType ref){
    tree->root = NULL;
    tree->refvalue = ref; 
} 

2、二叉樹的建立方式

// 建立二叉樹方式1 [node 是二級指標 C++可以透過BinTreeNode*& node引用的簡便寫法]
void createBinTree_1(BinTree* bt,BinTreeNode** node){
    ElemType item;
    scanf("%c",&item);
    if(item == bt->refvalue)
        *node = NULL;
    else{
        (*node) = (BinTreeNode*) malloc(sizeof(BinTreeNode));
        assert(*node != NULL);
        //建立根
        (*node)->data = item; 
        //遞迴建立
        createBinTree_1(bt,&(*node)->leftChild);
        createBinTree_1(bt,&(*node)->rightChild);
    }
}

//建立二叉樹方式2 透過返回根節點建立二叉樹
BinTreeNode* createBinTree_2(BinTree* bt){
    ElemType item;
    scanf("%c",&item);
    if(item == bt->refvalue)
        return NULL;
    else{
        BinTreeNode* node = (BinTreeNode*) malloc(sizeof(BinTreeNode));
        assert(node != NULL);
        node->data = item;
        node->leftChild = createBinTree_2(bt);
        node->rightChild = createBinTree_2(bt);
        return node;
    }
} 
//建立二叉樹方式3 根據字串str建立二叉樹
//此處是錯誤程式碼,原因:char* str  
//        1.createBinTree_3(bt,&(*node)->leftChild,++str);
//        2.createBinTree_3(bt,&(*node)->rightChild,++str);
//      在執行這兩行程式碼時,如果 在執行1時又遞迴呼叫了 1 ,
//        str不會進行改變,也就說不會"+2",在回來執行2時還是+1的狀態
//      所以此處不應該char* 想要改變一級指標的值就需要二級指標 
//void createBinTree_3(BinTree* bt,BinTreeNode** node,char* str){
//    if(*str == bt->refvalue){
//        *node = NULL;
//      ++str;
//    } else {
//        *node = (BinTreeNode*) malloc(sizeof(BinTreeNode));
//        assert(node != NULL);
//        (*node)->data = *str;
//        //遞迴建立
//      str++
//        createBinTree_3(bt,&(*node)->leftChild,str);
//        createBinTree_3(bt,&(*node)->rightChild,str);
//    }
//}
//改進 ABC##DE##F##G#H##
void createBinTree_3(BinTree* bt,BinTreeNode** node,char** str){
    if(**str == bt->refvalue){
        ++(*str);
        *node = NULL;
    } else {
        *node = (BinTreeNode*) malloc(sizeof(BinTreeNode));
        assert(node != NULL);
        (*node)->data = **str;
        //遞迴建立
        ++(*str);
        createBinTree_3(bt,&(*node)->leftChild,str);

        createBinTree_3(bt,&(*node)->rightChild,str);
    }
}

3、二叉樹遍歷

3.1、遞迴方式

//先序遍歷
void preOrder(BinTreeNode* node){
    if(node != NULL){
        printf("%c ",node->data);
        preOrder(node->leftChild);
        preOrder(node->rightChild);
    }
}

//中序遍歷
void inOrder(BinTreeNode* node){
    if(node != NULL){
        inOrder(node->leftChild);
        printf("%c ",node->data);
        inOrder(node->rightChild);
    }
} 

//後序遍歷
void postOrder(BinTreeNode* node){
    if(node != NULL){
        postOrder(node->leftChild);
        postOrder(node->rightChild);
        printf("%c ",node->data);
    }
}

3.2、非遞迴方式

//先序遍歷-不採用遞迴
void preOrder(BinTreeNode* node){
    if(node != NULL){
        SeqStack st;
        InitStack(&st);
        BinTreeNode* p;
        //根節點入棧 
        push(&st,node);
        while(!isEmpty(&st)){
            //當棧不為空
            //獲取棧頂 元素 
            GetTop(&st,&p);
            //出棧
            Pop(&st);
            //訪問元素 
            printf("%c ",p->data); 
            if(p->rightChild != NULL)
                //先將右節點入棧 
                push(&st,p->rightChild);
            if(p->leftChild != NULL)
                //在將左節點入棧 
                push(&st,p->leftChild);
        }
    }
} 

// 中序遍歷
void inOrder(BinTreeNode* node){
    if(node != NULL){
        SeqStack st;
        InitStack(&st);
        BinTreeNode* p;
        //根節點入棧 
        push(&st,node);
        while(! isEmpty(&st)){
            //將當前節點的左子樹入棧 
            while(node != NULL && node->leftChild != NULL){
                Push(&st,node->leftChild);
                node = node->leftChild;
            }
            //當訪問該節點的所有左子樹時 
            GetTop(&st,&p);
            Pop(&st);
            printf("%c ",p->data);
            //訪問完該節點的所有左子樹,將該節點的右子樹入棧 
            if(p->rightChild != NULL){
                node = p->rightChild;
                if(node != NULL)
                    Push(&st,node);
            }
        }
    }
} 
//後序遍歷-非遞迴 核心思想:將該節點的左右子樹都遍歷完才訪問該元素
//修改結構,增加標記位來記錄該節點從哪邊返回 
typedef enum{L,R} Tag;
typedef struct StkNode{
    BinTreeNode* prt;
    Tag tag;
}StkNode;

void postOrder(BinTreeNode* t){
    if(t != NULL){
        SeqStack st;
        InitStack(&st);
        StkNode sn;
        BinTreeNode* p;
        do{
            while(t != NULL){
                sn.prt = t;
                sn.tag = L;
                Push(&st,sn);
                t = t->leftChild;
            }
            bool flag = true;//是否繼續訪問的標記
            while(flag && !isEmpty(&st)){
                GetTop(&st, &sn);
                Pop(&st);
                p = sn->prt;
                switch(sn.tag){
                    case L:
                        //說明還沒訪問該節點的右子樹
                        sn->tag = R;
                        Push(&st,sn);
                        flag = false;//第一次右節點,先找該右節點的左子樹
                        t = p->rightChild;
                        break;  
                    case R:
                        //說明訪問過右子樹
                        printf("%c",p->data);
                        break; 
                }
            } 
        }while(!isEmpty(&st));
    }
} 

3.3、層次遍歷

//層次遍歷
void leverOrder(BinTreeNode* node){
    if(node == NULL)
        return; 
    BinTreeNode* v;
    LinkQueue queue;//宣告佇列
    initQueue(&queue);//初始化佇列
    enQueue(&queue,node);//入隊根節點
     
    while(!isEmpty(&queue)){
        //獲取隊頭元素 
        GetHead(&queue,&v);
        //出隊
        DeQueue(&queue);
        //訪問資料 
        printf("%c",v->data);
        //左節點入隊 
        if(v->leftChild != NULL)
            EnQueue(&queue,v->leftChild);
        //右節點入隊 
        if(v->rightChild != NULL)
            EnQueue(&queue,v->rightChild);
    }
}

4、其他常用方法實現

//二叉樹節點個數
int Size(BinTreeNode* node){
    if(node == NULL){
        return 0;
    }
    return Size(node->leftChild) + Size(node->rightChild) + 1;
}

//二叉樹高度
int Height(BinTreeNode* node) {
    if(node == NULL)
        return 0;
    else {
        int left_height = Height(node->leftChild);
        int right_height = Height(node->rightChild);
        return (left_height > right_height ? left_height : right_height) + 1;
    }
}

//查詢節點
BinTreeNode* Search(BinTreeNode* t,ElemType key) {
    if(t == NULL)
        return NULL;
    if(t->data == key)
        return t;
    BinTreeNode* p = Search(t->leftChild,key);
    if(p != NULL)
        return p;
    return Search(t->rightChild,key);
} 
//查詢節點的父節點
BinTreeNode* Parent(BinTreeNode* t,BinTreeNode* p){
    if(t == NULL || p == NULL)
        return NULL;
    if(t->leftChild == p || t->rightChild == p)
        return t;
    BinTreeNode* q = Parent(t->leftChild,p);
    if(q != NULL)
        return q;
    return Parent(t->leftChild,p);
} 

//求左子樹
BinTreeNode* leftChild(BinTreeNode* p){
    if(p != NULL)
        return p->leftChild;
    return NULL;
} 

//求右子樹
BinTreeNode* rightChild(BinTreeNode* p){
    if(p != NULL)
        return p->rightChild;
    return NULL;
} 

//判空
int BinTreeEmpty(BinTree* bt){
    return bt->root == NULL;
} 

//複製
void copy(BinTreeNode** bt1,BinTreeNode* bt2){
    if (bt2 == NULL) {
        *bt1 = NULL; // 如果 bt2 為空,則設定 *bt1 為空
    } else {
        *bt1 = (BinTreeNode*)malloc(sizeof(BinTreeNode)); // 分配新節點
        assert(*bt1 != NULL); // 確保記憶體分配成功
        (*bt1)->data = bt2->data; // 複製資料
        copy(&(*bt1)->leftChild, bt2->leftChild); // 遞迴複製左子樹
        copy(&(*bt1)->rightChild, bt2->rightChild); // 遞迴複製右子樹
    }
} 
//清除樹的所有節點
void BinTreeClear(BinTreeNode** t){
    if(*t != NULL){
        BinTreeClear(&(*t)->leftChild);
        BinTreeClear(&(*t)->rightChild);
        free(*t);
        *t = NULL;
    }
} 



int main(){
    //ABC##DE##F##G#H##
    BinTree myTree;
    initBinTree(&myTree,'#');
//    createBinTree_1(&myTree,&myTree.root);
//    myTree.root = createBinTree_2(&myTree);
    char* str = "ABC##DE##F##G#H##";
    createBinTree_3(&myTree,&myTree.root,&str);
    preOrder(myTree.root);
    printf("\n");
    inOrder(myTree.root);
    printf("\n");
    postOrder(myTree.root);
    printf("\n");
    printf("Size=%d\n",Size(myTree.root));
    printf("Height=%d\n",Height(myTree.root));
    
    BinTreeNode* p = Search(myTree.root,'B');
    BinTreeNode* q = Parent(myTree.root,p);
    printf("Parent=%c\n",q->data);
    
    return 0;
}

5、二叉樹的恢復實現(VLR_LVR_LRV)

//根據先序序列和中序序列生成二叉樹 
//root是一級指標 
void createBinTreeByVLRAndLVR(BinTreeNode** t,char* VLR,char* LVR,int n){
    if(n == 0)
        *t = NULL;
    else{
        int k = 0;
        //找到中序序列中的根節點位置 
        while(VLR[0] != LVR[k])
            k++;
        *t = (BinTreeNode*)malloc(sizeof(BinTreeNode));
        (*t)->data = LVR[k];
        //左子樹遞迴建立 
        createBinTreeByVLRAndLVR(&(*t)->leftChild,VLR+1,LVR,k);
        //右子樹遞迴建立
        // 先序遍歷中 VLR+k+1之前的都是左子樹的節點 
        createBinTreeByVLRAndLVR(&(*t)->rightChild,VLR + k + 1,LVR + k + 1,n - k - 1); 
    }
}

//根據中序序列和後序序列生成二叉樹 後序先訪問完左樹再右樹 
void createBinTreeByLVRAndLRV(BinTreeNode** t,char* LVR,char* LRV,int n){
    if(n == 0)
        *t = NULL;
    else{
        int k = 0;
        while(LRV[n - 1] != LVR[k])
            k++;
        *t = (BinTreeNode*)malloc(sizeof(BinTreeNode));
        (*t)->data = LVR[k];
        //右子樹遞迴建立 
        //當找到中序遍歷的根節點在k的位置時,說明k之前的節點都是左子樹節點,
        // 因為是後序遍歷,所以左子樹一定先顯示。故LRV+k 
        createBinTreeByLVRAndLRV(&(*t)->rightChild,LVR + k + 1,LRV + k,n - k - 1);
        //左子樹遞迴建立 
        createBinTreeByLVRAndLRV(&(*t)->leftChild,LVR,LRV,k);
    }
}

int main(){
    //先序序列 
    char* VLR = "ABCDEFGH";
    //中序序列
    char* LVR = "CBEDFAGH";
    //後序序列
    char* LRV = "CEFDBHGA";
    
    int n = strlen(VLR);
    BinTree myTree;
    initBinTree(&myTree,'#');
//    createBinTreeByVLRAndLVR(&myTree.root,VLR,LVR,n);

    createBinTreeByLVRAndLRV(&myTree.root,LVR,LRV,n);
    preOrder(myTree.root);
    printf("\n");
    inOrder(myTree.root);
    printf("\n");
    postOrder(myTree.root);
    printf("\n");
    return 0;
}

相關文章