Morris 遍歷二叉樹

Morris Traversal 方法實現前序、中序以及後序遍歷二叉樹。相比使用棧或者遞歸(也是經過棧空間)方法,Morris 方法能夠在空間複雜度爲 O(1),時間複雜度爲 O(n) 的條件下實現對二叉樹的遍歷。post

<!--more-->spa

前序遍歷

Binary-Tree-Preorder-Traversal

  1. 若是當前節點左孩子 cur->left 爲空,輸出當前節點 cur 並指向右孩子 cur->right。
  2. 若是當前節點左孩子 cur->left 不爲空,那麼在當前節點的左子樹中找出前驅節點 pre,也就是左子樹中最大的點。code

    • 若是前驅節點的右孩子 pre->right 爲空,那麼將右孩子指向當前節點。輸出當前節點。當前節點更新爲當前節點的左孩子。
    • 若是前驅節點的右孩子 pre->right 不爲空,也就是指向當前節點。從新將右孩子設爲空。當前節點更新爲當前節點的右孩子。
  3. 重複 一、2 直到當前節點爲空。
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        TreeNode* cur = root;
        TreeNode* pre = NULL;
        vector<int> result;
        while(cur!=NULL){
            if (cur->left == NULL){
                result.push_back(cur->val);
                cur = cur->right;
            }else{
                pre = cur->left;
                while(pre->right!=NULL && pre->right!=cur){
                    pre = pre->right;
                }
                if (pre->right==NULL){
                    pre->right=cur;
                    result.push_back(cur->val);
                    cur = cur->left;
                }else{
                    pre->right = NULL;
                    cur = cur->right;
                }
            }
        }
        return result;
    }
};

中序遍歷

Binary-Tree-Inorder-Traversal

  1. 若是當前節點左孩子 cur->left 爲空,輸出當前節點 cur 並指向右孩子 cur->right。
  2. 若是當前節點左孩子 cur->left 不爲空,那麼在當前節點的左子樹中找出前驅節點 pre,也就是左子樹中最大的點。遞歸

    • 若是前驅節點的右孩子 pre->right 爲空,那麼將右孩子指向當前節點。當前節點更新爲當前節點的左孩子。
    • 若是前驅節點的右孩子 pre->right 不爲空,也就是指向當前節點。從新將右孩子設爲空。輸出當前節點。當前節點更新爲當前節點的右孩子。
  3. 重複 一、2 直到當前節點爲空。
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        TreeNode* cur = root;
        TreeNode* pre = NULL;
        vector<int> result;
        while(cur!=NULL){
            if (cur->left == NULL){
                result.push_back(cur->val);
                cur = cur->right;
            }else{
                pre = cur->left;
                while(pre->right!=NULL && pre->right!=cur){
                    pre = pre->right;
                }
                if (pre->right == NULL){
                    pre->right=cur;
                    cur = cur->left;
                }else{
                    pre->right = NULL;
                    result.push_back(cur->val);
                    cur = cur->right;
                }
            }
        }
        return result;
    }
};

後序遍歷

Binary-Tree-Postorder-Traversal

  1. 新增臨時節點 dump,而且將 root 設爲 dump 的左孩子。
  2. 若是當前節點左孩子 cur->left 爲空,輸出當前節點 cur 並指向右孩子 cur->right。
  3. 若是當前節點左孩子 cur->left 不爲空,那麼在當前節點的左子樹中找出前驅節點 pre,也就是左子樹中最大的點。rem

    • 若是前驅節點的右孩子 pre->right 爲空,那麼將右孩子指向當前節點。當前節點更新爲當前節點的左孩子。
    • 若是前驅節點的右孩子 pre->right 不爲空,也就是指向當前節點。逆序輸出當前節點左孩子到前序節點的路徑。從新將右孩子設爲空。當前節點更新爲當前節點的右孩子。
  4. 重複 二、3 直到當前節點爲空。

逆序打印路徑其實就是逆序打印單鏈表節點。先將單鏈表反轉,而後依次打印,接下來從新反轉到初始狀態。get

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        TreeNode dump(-1);
        dump.left = root;
        TreeNode* cur = &dump;
        TreeNode* pre = NULL;
        vector<int> result;
        while(cur!=NULL){
            if (cur->left == NULL){
                cur = cur->right;
            }else{
                pre = cur->left;
                while(pre->right!=NULL && pre->right!=cur){
                    pre = pre->right;
                }
                if (pre->right == NULL){
                    pre->right=cur;
                    cur = cur->left;
                }else{
                    printReverse(cur->left, pre, result);
                    pre->right = NULL;
                    cur = cur->right;
                }
            }
        }
        return result;
    }

    void printReverse(TreeNode* from, TreeNode* to, vector<int>& result){
        Reverse(from, to);
        TreeNode* p = to;
        while(true){
            result.push_back(p->val);
            if(p == from){
                break;
            }
            p = p->right;
        }
        Reverse(to, from);
    }

    void Reverse(TreeNode* from, TreeNode* to){
        TreeNode* x = from;
        TreeNode* y = from->right;
        TreeNode* z;
        if (from == to){
            return;
        }
        x->right = NULL;
        while(x != to){
            z = y->right;
            y->right = x;
            x = y;
            y = z;
        }
    }
};

總結

Morris 方法遍歷之因此可以在 O(1) 的空間的條件下完成是由於它充分利用到葉子的左右孩子來記錄上層關係,從而不須要額外的棧空間來記錄順序關係。經過三種遍歷能夠看到,其實整體上的代碼邏輯沒有發生改變,主要是改變了輸出結果的時機和方式。it

https://www.ouyangsong.com/po...io

相關文章
相關標籤/搜索