二叉樹先序、中序和後序遍歷

轉載自 http://blog.csdn.net/quzhongxin/article/details/46315251


Binary Tree Preorder Traversal:https://leetcode.com/problems/binary-tree-preorder-traversal/ 
Binary Tree Inorder Traversal :https://leetcode.com/problems/binary-tree-inorder-traversal/ 
Binary Tree Postorder Traversal:https://leetcode.com/problems/binary-tree-postorder-traversal/


  • 前序遍歷:先訪問該節點,然後訪問該節點的左子樹和右子樹;
  • 中序遍歷:先訪問該節點的左子樹,然後訪問該節點,再訪問該節點的右子樹;
  • 後序遍歷:想訪問該節點的左子樹和右子樹,然後訪問該節點。

遞歸遍歷

對於遞歸遍歷比較簡單:

void preorder(TreeNode* root) {
    if (root == NULL)
        return;
    visit(root);
    preorder(root->left);
    preorder(root->right);
}

void inorder(TreeNode* root) {
    if (root == NULL)
        return;
    inorder(root->left);
    visit(root);
    inorder(root-<right);
}

void postorder(TreeNode* root) {
    if (root == NULL)
        return;
    postorder(root->left);
    postorder(root->right);
    visit(root);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

非遞歸(迭代)遍歷

非遞歸實現在遍歷根節點後還要回來,因此要基於棧(先進後出)來保存節點。 
二叉樹遍歷的非遞歸實現文章裏有不同的實現方式,更易於理解記憶。

前序遍歷

壓入順序:右子樹->左子樹->根節點 
使得訪問的時候的順序成爲:根->左子樹->右子樹

    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> s;
        if (root == NULL)
            return result;
        s.push(root);
        while(!s.empty()) {
            TreeNode* p = s.top();
            s.pop();
            result.push_back(p->val);
            if (p->right)
                s.push(p->right);
            if (p->left)
                s.push(p->left);
        }
        return result;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

中序遍歷

壓入順序:右子樹->根->左子樹 
只有當左子樹已經訪問完後,才能訪問根節點

對於任一結點P, 
1)若其左孩子不爲空,則將P入棧並將P的左孩子置爲當前的P,然後對當前結點P再進行相同的處理; 
2)若其左孩子爲空,則取棧頂元素並進行出棧操作,訪問該棧頂結點,然後將當前的P置爲棧頂結點的右孩子; 
3)直到P爲NULL並且棧爲空則遍歷結束

vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> s;
        if (root == NULL)
            return result;
        TreeNode* p = root;
        while (!s.empty() || p != NULL) {
            if (p != NULL) {
                // push 左子樹入棧
                s.push(p);
                p = p->left;
            } else {
                // 左子樹爲空時,訪問該節點,然後訪問右子樹
                p = s.top();
                result.push_back(p->val);
                s.pop();
                p = p->right;
            }
        }
        return result;  
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

後序遍歷

先壓入根,然後是右子樹,最後左子樹 
要求最後訪問根節點,即訪問該根節點時必須訪問完左子樹和右子樹,我們只需要保證訪問某一節點時,該節點的右子樹已經被訪問,否則需要將該節點重新壓入棧。

對於任一結點P,將其入棧,然後沿其左子樹一直往下搜索,直到搜索到沒有左孩子的結點,此時該結點出現在棧頂,但是此時不能將其出棧並訪問,因此其右孩子還爲被訪問。所以接下來按照相同的規則對其右子樹進行相同的處理,當訪問完其右孩子時,該結點又出現在棧頂,此時可以將其出棧並訪問。這樣就保證了正確的訪問順序。可以看出,在這個過程中,每個結點都兩次出現在棧頂,只有在第二次出現在棧頂時,才能訪問它。

  vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        if (root == NULL)
            return result;
        stack<TreeNode*> s;
        TreeNode* p = root;  //當前正訪問的節點
        TreeNode* q;  //記錄剛剛訪問過的節點
        do{
            while (p != NULL) {
                s.push(p);
                p = p->left;
            }
            q = NULL;
            while (!s.empty()) {
                p = s.top();
                s.pop();
                if (p->right == q) {  //當右子樹已經訪問過了,纔可以訪問根
                    result.push_back(p->val);
                    q = p;  //記錄剛剛訪問過的節點
                } else {
                    s.push(p); //第一次訪問到該節點,需要將它重新入棧
                    p = p->right;
                    break;
                }

            }
        } while (!s.empty());
        return result;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

參考資料 
二叉樹的非遞歸遍歷 http://www.cnblogs.com/dolphin0520/archive/2011/08/25/2153720.html


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