二叉樹的三種遍歷(前序/中序/后序)

參考博客

/*
* 二叉樹的先序遍歷 --- 非遞歸版與遞歸版
*/


#include <iostream>
#include <stack>
using namespace std;

struct TreeNode{
    int val;
    TreeNode* left, *right;
    TreeNode(int v = 0) : val(v), left(NULL), right(NULL){}
};

void visit(TreeNode* p){ //訪問函數,這里簡單只設為是打印
    cout << p->val << " ";
}

/*******************************************
*               前序遍歷
********************************************/
void BT_PreOrder(TreeNode* root){
    if(root == NULL) return;
    stack<TreeNode*> S;
    S.push(root);

    while(!S.empty()){
        TreeNode* cur = S.top();
        S.pop();
        visit(cur); //訪問
        if(cur->right) S.push(cur->right);
        if(cur->left) S.push(cur->left);
    }
    return;
}


/*******************************************
*               中序遍歷
********************************************/
void BT_InOrder(TreeNode *root){
    stack<TreeNode* > S;
    TreeNode *cur = root;
    while(true){
        while(cur){
            S.push(cur); cur = cur->left;
        }
        if(S.empty()) break;

        cur = S.top();
        visit(cur);
        S.pop();
        cur = cur->right;
    }
}

/*******************************************
*               后序遍歷
********************************************/

void findFirst(stack<TreeNode*> &S); //找到以棧頂節點為根的子樹其第一個應該輸出的節點   
 //即按照自己-右節點-左節點的順序壓棧

void BT_PostOrder(TreeNode* root){
    if(root == NULL) return;
    
    stack<TreeNode *> S;
    TreeNode *cur = root;
    S.push(root);
    while(!S.empty()){
        if(S.top()->left != cur && S.top()->right != cur){ //當前節點不是棧頂節點的子節點
        //這里的當前節點指的是上一個輸出的節點
        //這種關系表示上一個輸出節點是左節點,而棧頂是右節點,因為右節點可能有未輸出的子樹,因此需要進行FindFirst的操作
            findFirst(S); 
        }
        visit(cur = S.top());
        S.pop();
    }
    
}
void findFirst(stack<TreeNode*> &S) {
    //找到以棧頂節點為根的子樹其第一個應該輸出的節點
    while (TreeNode* x = S.top()) {
        if (x->left) { //一句話總結,有左先右再左,無左只壓右
            if (x->right) S.push(x->right);
            S.push(x->left);
        }
        else {
            S.push(x->right);
        }
    }
    S.pop(); //刪掉棧頂的空指針
}

//前序遍歷的遞歸定義:先根節點,后左子樹,再右子樹。
//首先,我們遍歷左子樹,邊遍歷邊打印,并把根節點存入棧中,以后需借助這些節點進入右子樹開啟新一輪的循環。
void my_pre(TreeNode* root){
    if(root == NULL)
        return;
    TreeNode* p = root;
    stack<TreeNode*> s;
    while(p||!s.empty()){
        while(p){
            visit(p);
            s.push(p);
            p=p->left;
        }
        if(!s.empty()){
            p = s.top();
            s.pop();
            p = p->right;
        }
    }
}

void my_in(TreeNode* root){
    if(root == NULL)
        return;
    TreeNode* p = root;
    stack<TreeNode*> s;
    while(p||!s.empty()){
        //一直遍歷到左子樹最下邊,邊遍歷邊保存根節點到棧中 
        if(p){
            s.push(p);
            p=p->left;
        }
        //當p為空時,說明已經到達左子樹最下邊,這時需要出棧了
        else{
            p = s.top();
            s.pop();
            visit(p);
            //進入右子樹,開始新的一輪左子樹遍歷(這是遞歸的自我實現)  
            p = p->right;
        }
    }
}

/*
0
|
+ 2 ----- 6
|   +---- 5 ---- 10
|         + ---- 9
+ 1 ----- 4 ---- 8
          + ---- 7
    +---- 3       
*/

int main(){
    TreeNode n0(0), n1(1), n2(2), n3(3), n4(4),n5(5), n6(6), n7(7),n8(8), n9(9),n10(10);
    n0.left = &n1; n0.right = &n2;
    n1.left = &n3; n1.right = &n4;
    n2.left = &n5; n2.right = &n6;
    n4.left = &n7; n4.right = &n8;
    n5.left = &n9; n5.right = &n10;

    BT_PreOrder(&n0);
    cout << endl;
    my_pre(&n0);
    cout << endl;

    BT_InOrder(&n0);
    cout << endl;
    my_in(&n0);
    cout << endl;

    BT_PostOrder(&n0);
    cout << endl;
}

二叉樹的非遞歸后續遍歷

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> s;
        vector<int> result;
        TreeNode* p = root; 
        TreeNode* q = nullptr;  //用來保存剛訪問過的節點
        do{
            while(p!=nullptr){    //往左下走到底
                s.push(p);
                p = p->left;
            }
            q = nullptr;
            while(!s.empty()){
                p = s.top();
                s.pop();
                if(p->right == q){    //p為左孩子或者說是可以訪問的父節點(右子樹已被訪問)
                    result.push_back(p->val);
                    q = p;  // 保存剛剛訪問過的節點
                }
                else {  //  當前節點不能訪問,需要第二次進棧
                    s.push(p);
                    p = p->right; //先處理右子樹
                    break;
                }
            }
            
            
        }while(!s.empty());
        return result;
    
    }
};
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容