1. 程式人生 > 實用技巧 >【C語言】二叉樹中序遍歷(遞迴和非遞迴)演算法

【C語言】二叉樹中序遍歷(遞迴和非遞迴)演算法

二叉樹中序遍歷的實現思想是:

  1. 訪問當前節點的左子樹;
  2. 訪問根節點;
  3. 訪問當前節點的右子樹;

圖 1 二叉樹

以圖 1 為例,採用中序遍歷的思想遍歷該二叉樹的過程為:

  1. 訪問該二叉樹的根節點,找到 1;
  2. 遍歷節點 1 的左子樹,找到節點 2;
  3. 遍歷節點 2 的左子樹,找到節點 4;
  4. 由於節點 4 無左孩子,因此找到節點 4,並遍歷節點 4 的右子樹;
  5. 由於節點 4 無右子樹,因此節點 2 的左子樹遍歷完成,訪問節點 2;
  6. 遍歷節點 2 的右子樹,找到節點 5;
  7. 由於節點 5 無左子樹,因此訪問節點 5 ,又因為節點 5 沒有右子樹,因此節點 1 的左子樹遍歷完成,訪問節點 1 ,並遍歷節點 1 的右子樹,找到節點 3;
  8. 遍歷節點 3 的左子樹,找到節點 6;
  9. 由於節點 6 無左子樹,因此訪問節點 6,又因為該節點無右子樹,因此節點 3 的左子樹遍歷完成,開始訪問節點 3 ,並遍歷節點 3 的右子樹,找到節點 7;
  10. 由於節點 7 無左子樹,因此訪問節點 7,又因為該節點無右子樹,因此節點 1 的右子樹遍歷完成,即整棵樹遍歷完成;


因此,圖 1 中二叉樹採用中序遍歷得到的序列為:

4 2 5 1 6 3 7

遞迴實現

二叉樹的中序遍歷採用的是遞迴的思想,因此可以遞迴實現,其 C 語言實現程式碼為:

#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 INOrderTraverse(BiTree T){ if (T) { INOrderTraverse(T->lchild);//遍歷左孩子 displayElem(T);//呼叫操作結點資料的函式方法 INOrderTraverse(T->rchild);//遍歷右孩子 } //如果結點為空,返回上一層 return; } int main() { BiTree Tree; CreateBiTree(&Tree); printf("中序遍歷演算法: \n"); INOrderTraverse(Tree); }
執行結果:
中序遍歷演算法:
4 2 5 1 6 3 7

非遞迴實現

而遞迴的底層實現依靠的是棧儲存結構,因此,二叉樹的先序遍歷既可以直接採用遞迴思想實現,也可以使用棧的儲存結構模擬遞迴的思想實現。

中序遍歷的非遞迴方式實現思想是:從根結點開始,遍歷左孩子同時壓棧,當遍歷結束,說明當前遍歷的結點沒有左孩子,從棧中取出來呼叫操作函式,然後訪問該結點的右孩子,繼續以上重複性的操作。

除此之外,還有另一種實現思想:中序遍歷過程中,只需將每個結點的左子樹壓棧即可,右子樹不需要壓棧。當結點的左子樹遍歷完成後,只需要以棧頂結點的右孩子為根結點,繼續迴圈遍歷即可。

兩種非遞迴方法實現二叉樹中序遍歷的程式碼實現為:

#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 push(BiTNode** a,BiTNode* elem){
    a[++top]=elem;
}
//彈棧函式
void pop( ){
    if (top==-1) {
        return ;
    }
    top--;
}
//模擬操作結點元素的函式,輸出結點本身的數值
void displayElem(BiTNode* elem){
    printf("%d ",elem->data);
}
//拿到棧頂元素
BiTNode* getTop(BiTNode**a){
    return a[top];
}
//中序遍歷非遞迴演算法
void InOrderTraverse1(BiTree Tree){
    BiTNode* a[20];//定義一個順序棧
    BiTNode * p;//臨時指標
    push(a, Tree);//根結點進棧
    while (top!=-1) {//top!=-1說明棧內不為空,程式繼續執行
        while ((p=getTop(a)) &&p){//取棧頂元素,且不能為NULL
            push(a, p->lchild);//將該結點的左孩子進棧,如果沒有左孩子,NULL進棧
        }
        pop();//跳出迴圈,棧頂元素肯定為NULL,將NULL彈棧
        if (top!=-1) {
            p=getTop(a);//取棧頂元素
            pop();//棧頂元素彈棧
            displayElem(p);
            push(a, p->rchild);//將p指向的結點的右孩子進棧
        }
    }
}
//中序遍歷實現的另一種方法
void InOrderTraverse2(BiTree Tree){
    BiTNode* a[20];//定義一個順序棧
    BiTNode * p;//臨時指標
    p=Tree;
    //當p為NULL或者棧為空時,表明樹遍歷完成
    while (p || top!=-1) {
        //如果p不為NULL,將其壓棧並遍歷其左子樹
        if (p) {
            push(a, p);
            p=p->lchild;
        }
        //如果p==NULL,表明左子樹遍歷完成,需要遍歷上一層結點的右子樹
        else{
            p=getTop(a);
            pop();
            displayElem(p);
            p=p->rchild;
        }
    }
}
int main(){
    BiTree Tree;
    CreateBiTree(&Tree);
    printf("中序遍歷演算法1: \n");
    InOrderTraverse1(Tree);
    printf("\n中序遍歷演算法2: \n");
    InOrderTraverse2(Tree);
}

執行結果

中序遍歷演算法1:
4 2 5 1 6 3 7
中序遍歷演算法2:
4 2 5 1 6 3 7