二叉樹三種遍歷方式(遞歸和非遞歸)

樹形結構是一類重要的非線性數據結構。其中以樹和二叉樹是最爲常用。

二叉樹有四種遍歷順序:先序遍歷(前序遍歷),中序遍歷,後序遍歷,層序遍歷。

這三種遍歷的方式其實是由遍歷的根結點的順序來定義的。

先序遍歷:先訪問根結點,再遍歷它的左子樹,最後遍歷它的右子樹。

中序遍歷:先遍歷左子樹,然後訪問根結點,最後遍歷它的右子樹。並且在遍歷左右子樹的時候。仍然是先遍歷左子樹,然後訪問根節點,最後遍歷右子樹。

後序遍歷:先遍歷左子樹,然後遍歷右子樹,最後訪問根結點。在遍歷左右子樹的時候同樣要先遍歷左子樹,然後遍歷右子樹,最後訪問根節點。

層序遍歷:從上往下逐層進行遍歷。

來看一個例子:

先定義二叉樹的每個結點的數據域和指針域:

struct node{
    int v; //數據域
    node *lson; //當前節點的左子結點
    node *rson;//當前節點的右子結點
};

上圖所示就是一棵二叉樹,那麼現在來遍歷這棵二叉樹。

先序遍歷:根據前面所述先序遍歷的定義,就會先遍歷A,輸出A,然後遍歷A的左子結點B,輸出B,此時又會將B看成一棵新的二叉樹的根結點,再遍歷B的左子結點C,輸出C,發現C沒有任何子結點了,就會回到B,再遍歷B的右子結點D,輸出D,發現D沒有任何子結點了,然後回到A,再遍歷A的右子結點,輸出E,然後發現E沒有左子結點,回到E,繼續遍歷E的右子結點F,輸出F,最後發現F沒有任何子結點了,至此,先序遍歷結束,所得到的結果就是ABCDEF

 

中序遍歷:先來到根結點A,然後遍歷它的左子樹,來到結點B,再遍歷B的左子樹,到結點C,繼續遍歷C的左子樹,發現沒有,然後返回到結點C,輸出C,再遍歷C的右子樹,發現也沒有,然後回到結點B,輸出B,再遍歷B的右子樹,來到結點D,繼續遍歷D的左子樹,沒有,然後回到D,輸出D,再遍歷D的右子樹也沒有,然後回到D,B的左右子樹已遍歷完,再返回上一層到結點A,此時,A的左子樹已經全部遍歷完,所以訪問根結點,輸出A,再開始遍歷A的右子樹,來到E,遍歷E的左子樹,發現爲空,返回E,輸出E,再遍歷E的右子樹,到F,遍歷F的左子樹,發現爲空,返回到F,輸出F,再遍歷F的右子樹,同樣爲空,又返回F,然後E的子樹也遍歷完,返回A,至此,中序遍歷結束,所得到的結果是CBDAEF

 

後序遍歷:先到根結點A,然後遍歷它的左子樹,來到結點B,再遍歷B的左子樹,到結點C,繼續遍歷C的左子樹,發現沒有,然後返回到結點C,再遍歷C的右子樹,發現也沒有,然後返回到結點C,輸出C,再遍歷B的右子樹,來到結點D,繼續遍歷D的左右子樹,發現均爲空,於是返回到D,輸出D,B的左右子樹遍歷完,又返回到B,輸出B,A的左子樹已遍歷完,然後遍歷A的右子樹,到結點E,遍歷E的左子樹,發現爲空,返回到E,再遍歷E的右子樹,來到F,遍歷F的左右子樹,發現均爲空,返回到F,輸出F,然後E的左右子樹均已遍歷完,有返回到E,輸出E,此時A的左右子樹已遍歷完,回到A,輸出A,至此,後續遍歷結束,得到的結果爲CDBFEA

 

層序遍歷:從上往下從左往右依次遍歷,結果就是ABECDF

 

接下來給出三種遍歷方式的遞歸版代碼:

先序遍歷:

void preOrder(node *root)
{
    if(root == NULL) return;
    cout<<root->v<<" ";
    preOrder(root->lson);
    preOrder(root->rson);
}

 

中序遍歷:

void inOrder(node *root)
{
    if(root == NULL) return;
    inOrder(root->lson);
    cout<<root->v<<" ";
    inOrder(root->rson);
}

 

後序遍歷:

void posOrder(node *root)
{
    if(root == NULL) return;
    posOrder(root->lson);
    posOrder(root->rson);
    cout<<root->v<<" ";
}

 

下面給出三種遍歷方式的非遞歸版:

先序遍歷:

1.申請一個空棧,將祖先結點壓入棧中。

2.彈出棧頂元素,因爲是先序遍歷,所以直接輸出這個棧頂元素。因爲是先序遍歷的遍歷順序是根-->左-->右,但是因爲棧是先進後出,所以應該先推入右子結點,再推入左子結點(如果有左子結點和右子結點的話)。

3.不斷重複第2步,直到棧爲空。

void preOrder(node *root)
{
    stack<node*>sk;
    while(!sk.empty()) sk.pop();
    sk.push(root);
    while(!sk.empty()){
        node *cur = sk.top();//棧頂元素是當前的結點
        sk.pop();//彈出棧頂元素
        cout<<cur->v<<" ";
        if(cur->rson!=NULL) sk.push(cur->rson);
        if(cur->lson!=NULL) sk.push(cur->lson);
    }
}

中序遍歷:

1.申請一個空棧,將祖先結點壓入棧中。

2.如果當前結點不爲空,就將結點壓入棧,然後讓結點變爲當前結點的左子結點。如果當前結點爲空,就取出棧頂元素並打印,然後彈出棧頂元素,再讓當前結點變爲當前結點的右子結點。

3.不斷重複第2步,直到棧空並且當前結點爲空的時候。

void Inorder(node *root)
{
    stack<node*>sk;
    while(!sk.empty()) sk.pop();
    while(!sk.empty() || root!=NULL){
        if(root == NULL){
            node *cur = sk.top();
            sk.pop();
            cout<<cur->v<<" ";
            root = cur->rson;
        }else{
            sk.push(root);
            root = root->lson;
        }
    }
}

後序遍歷:

1.創建兩個空棧,一個棧保存結點元素,一個棧保存輸出的答案。

2.如果棧不爲空,就彈出存結點元素的棧頂記爲cur,然後將棧頂元素出棧,並且將cur壓入存儲答案的棧中,如果cur有左子結點就將左子結點壓入存結點元素的棧中,如果有右子結點,也將右子結點壓入這個棧中。

3.重複第2步,直到棧空。

void posOrder(node *root)
{
    stack<node*>sk; //保存結點元素
    stack<node*>res; //保存輸出的元素
    while(!sk.empty()) sk.pop();
    while(!res.empty()) res.pop();
    sk.push(root);
    while(!sk.empty()){
        node *cur = sk.top();
        sk.pop();
        res.push(cur);
        if(cur->lson != NULL) sk.push(cur->lson);
        if(cur->rson != NULL) sk.push(cur->rson);
    }
    while(!res.empty()){
        cout<<res.top()->v<<" ";
        res.pop();
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章