1. 程式人生 > >二叉樹後序遍歷(遞迴與非遞迴)演算法及C語言實現

二叉樹後序遍歷(遞迴與非遞迴)演算法及C語言實現

二叉樹後序遍歷的實現思想是:從根節點出發,依次遍歷各節點的左右子樹,直到當前節點左右子樹遍歷完成後,才訪問該節點元素。


圖 1 二叉樹
  如圖 1 中,對此二叉樹進行後序遍歷的操作過程為:
  • 從根節點 1 開始,遍歷該節點的左子樹(以節點 2 為根節點);
  • 遍歷節點 2 的左子樹(以節點 4 為根節點);
  • 由於節點 4 既沒有左子樹,也沒有右子樹,此時訪問該節點中的元素 4,並回退到節點 2 ,遍歷節點 2 的右子樹(以 5 為根節點);
  • 由於節點 5 無左右子樹,因此可以訪問節點 5 ,並且此時節點 2 的左右子樹也遍歷完成,因此也可以訪問節點 2;
  • 此時回退到節點 1 ,開始遍歷節點 1 的右子樹(以節點 3 為根節點);
  • 遍歷節點 3 的左子樹(以節點 6 為根節點);
  • 由於節點 6 無左右子樹,因此訪問節點 6,並回退到節點 3,開始遍歷節點 3 的右子樹(以節點 7 為根節點);
  • 由於節點 7 無左右子樹,因此訪問節點 7,並且節點 3 的左右子樹也遍歷完成,可以訪問節點 3;節點 1 的左右子樹也遍歷完成,可以訪問節點 1;
  • 到此,整棵樹的遍歷結束。
由此,對圖 1 中二叉樹進行後序遍歷的結果為:

4 5 2 6 7 3 1

遞迴實現

後序遍歷的遞迴實現程式碼為:
#include <stdio.h>
#include <string.h>
#define TElemType int
//構造結點的結構體
typedef struct BiTNode{
    TElemType data;//資料域
    struct BiTNode *lchild,*rchild;//左右孩子指標
}BiTNode,*BiTree;
//初始化樹的函式
void CreateBiTree(BiTree *T){
    *T=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->data=1;
    (*T)->lchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->rchild=(BiTNode*)malloc(sizeof(BiTNode));
  
    (*T)->lchild->data=2;
    (*T)->lchild->lchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->lchild->rchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->lchild->rchild->data=5;
    (*T)->lchild->rchild->lchild=NULL;
    (*T)->lchild->rchild->rchild=NULL;
    (*T)->rchild->data=3;
    (*T)->rchild->lchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->rchild->lchild->data=6;
    (*T)->rchild->lchild->lchild=NULL;
    (*T)->rchild->lchild->rchild=NULL;
    (*T)->rchild->rchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->rchild->rchild->data=7;
    (*T)->rchild->rchild->lchild=NULL;
    (*T)->rchild->rchild->rchild=NULL;
    (*T)->lchild->lchild->data=4;
    (*T)->lchild->lchild->lchild=NULL;
    (*T)->lchild->lchild->rchild=NULL;
}

//模擬操作結點元素的函式,輸出結點本身的數值
void displayElem(BiTNode* elem){
    printf("%d ",elem->data);
}
//後序遍歷
void PostOrderTraverse(BiTree T){
    if (T) {
        PostOrderTraverse(T->lchild);//遍歷左孩子
        PostOrderTraverse(T->rchild);//遍歷右孩子
        displayElem(T);//呼叫操作結點資料的函式方法
    }
    //如果結點為空,返回上一層
    return;
}
int main() {
    BiTree Tree;
    CreateBiTree(&Tree);
    printf("後序遍歷: \n");
    PostOrderTraverse(Tree);
}
執行結果: 後序遍歷:
4 5 2 6 7 3 1

非遞迴實現

遞迴演算法底層的實現使用的是儲存結構,所以可以直接使用棧寫出相應的非遞迴演算法。

後序遍歷是在遍歷完當前結點的左右孩子之後,才呼叫操作函式,所以需要在操作結點進棧時,為每個結點配備一個標誌位。當遍歷該結點的左孩子時,設定當前結點的標誌位為 0,進棧;當要遍歷該結點的右孩子時,設定當前結點的標誌位為 1,進棧。

這樣,當遍歷完成,該結點彈棧時,檢視該結點的標誌位的值:如果是 0,表示該結點的右孩子還沒有遍歷;反之如果是 1,說明該結點的左右孩子都遍歷完成,可以呼叫操作函式。

完整實現程式碼為:
#include <stdio.h>
#include <string.h>
#define TElemType int
int top=-1;//top變數時刻表示棧頂元素所在位置
//構造結點的結構體
typedef struct BiTNode{
    TElemType data;//資料域
    struct BiTNode *lchild,*rchild;//左右孩子指標
}BiTNode,*BiTree;
//初始化樹的函式
void CreateBiTree(BiTree *T){
    *T=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->data=1;
    (*T)->lchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->rchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->lchild->data=2;
    (*T)->lchild->lchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->lchild->rchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->lchild->rchild->data=5;
    (*T)->lchild->rchild->lchild=NULL;
    (*T)->lchild->rchild->rchild=NULL;
    (*T)->rchild->data=3;
    (*T)->rchild->lchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->rchild->lchild->data=6;
    (*T)->rchild->lchild->lchild=NULL;
    (*T)->rchild->lchild->rchild=NULL;
    (*T)->rchild->rchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->rchild->rchild->data=7;
    (*T)->rchild->rchild->lchild=NULL;
    (*T)->rchild->rchild->rchild=NULL;
    (*T)->lchild->lchild->data=4;
    (*T)->lchild->lchild->lchild=NULL;
    (*T)->lchild->lchild->rchild=NULL;
}
//彈棧函式
void pop( ){
    if (top==-1) {
        return ;
    }
    top--;
}
//模擬操作結點元素的函式,輸出結點本身的數值
void displayElem(BiTNode* elem){
    printf("%d ",elem->data);
}

//後序遍歷非遞迴演算法
typedef struct SNode{
    BiTree p;
    int tag;
}SNode;
//後序遍歷使用的進棧函式
void postpush(SNode *a,SNode sdata){
    a[++top]=sdata;
}
//後序遍歷函式
void PostOrderTraverse(BiTree Tree){
    SNode a[20];//定義一個
順序棧
BiTNode * p;//臨時指標 int tag; SNode sdata; p=Tree; while (p||top!=-1) { while (p) { //為該結點入棧做準備 sdata.p=p; sdata.tag=0;//由於遍歷是左孩子,設定標誌位為0 postpush(a, sdata);//壓棧 p=p->lchild;//以該結點為根結點,遍歷左孩子 } sdata=a[top];//取棧頂元素 pop();//棧頂元素彈棧 p=sdata.p; tag=sdata.tag; //如果tag==0,說明該結點還沒有遍歷它的右孩子 if (tag==0) { sdata.p=p; sdata.tag=1; postpush(a, sdata);//更改該結點的標誌位,重新壓棧 p=p->rchild;//以該結點的右孩子為根結點,重複迴圈 } //如果取出來的棧頂元素的tag==1,說明此結點左右子樹都遍歷完了,可以呼叫操作函數了 else{ displayElem(p); p=NULL; } } } int main(){ BiTree Tree; CreateBiTree(&Tree); printf("後序遍歷: \n"); PostOrderTraverse(Tree); }
執行結果 後序遍歷:
4 5 2 6 7 3 1