Pre, In, Post Order Traversal

2016-08-29  本文已影响0人  withacup

Related problem:

144 Binary Tree Preorder Traversal
94 Binary Tree Inorder Traversal
145 Binary Tree Postorder Traversal
99 Recover Binary Search Tree

在写tree的算法之前,前中后序遍历的 recursive 写法和 iterative 写法必须熟练:

You can find the definitions for the three traversals here

/*
 Definition for a binary tree node.
 struct TreeNode {
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 };
 */
//preOrder
void preorder(TreeNode* root) {
        if (!root) return; // '!root' means 'when root == nullptr'
        cout << root -> val << endl;
        preorder(root -> left);
        preorder(root -> right);
}
//inOrder
void inorder(TreeNode* root) {
        if (!root) return;
        inorder(root -> left);
        cout << root -> val << endl;
        inorder(root -> right);
}
//postOrder
void postorder(TreeNode* root) {
        if (!root) return;
        postorder(root -> left);
        postorder(root -> right);
        cout << root -> val << endl;
}

But you will never get a chance to use these recursive version in a real interview, what really matters is the iterative solution below:


My thoughts:

preorder: we need to make sure the value in the current TreeNode has been visited before we get to left and right subtree, and we need to make sure the whole left subtree has been visited before we visit right subtree.


Inorder: we need to make sure the whole left subtree has been visited before we visit current node, then we visit right subtree node.


postorder: we need to make sure all the children nodes for the current node has been visited before we visit current node, and the left subtree is visited before right subtree.

class Solution {
    void pushLeft(TreeNode* root, stack<TreeNode*>& nodes, vector<int>& res) {
    // this function here push all the LEFT node we can get from the input node
        while (root) {
            nodes.emplace(root); // emplace c++11
            res.emplace_back(root -> val); // visit the node once we meet it
            root = root -> left;
        }
    }
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        if (!root) return res;
        stack<TreeNode*> nodes; // store the parent node, 
                               //all nodes in the stack has already been visited
        pushLeft(root, nodes, res);
        while (!nodes.empty()) {
            TreeNode* cur = nodes.top() -> right; // cause nodes in the stack 
// has already been visited, we just need to visit the right subtree
            nodes.pop();
            if (cur) // visit the left subtree for the right children
                pushLeft(cur, nodes, res);
        }
        return res;
    }
};

class Solution {
      void pushLeft(TreeNode* root, stack<TreeNode*>& nodes) {
  // this function here push all the LEFT node we can get from the input node
        while (root) {
            nodes.emplace(root); // emplace c++11
            root = root -> left;
        }
    }
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        if (!root) return res;
        stack<TreeNode*> nodes; // store the parent node, 
                             //all nodes in the stack has already been visited
        pushLeft(root, nodes);
        while (!nodes.empty()) {
            TreeNode* cur = nodes.top() -> right; 
            // nodes in the stack has not been visited yet  

            res.emplace_back(nodes.top() -> val); // a node can get to the top postion
// if and only if it doesn't have a left children or all its left subtree has been
// visited, so it is safe to visit top node now

            nodes.pop(); // once we visited the node, pop it

            if (cur) // store the left subtree for the right children
                pushLeft(cur, nodes);
        }
        return res;
    }
};

Notice: the only difference between above two iterative algorithms is the position of res.emplace_back(nodes.top() -> val), so actually I'm using the same idea used in recursive solution. But it's gonna be a different story when it comes to postorder.

class Solution {
void pushLeft(TreeNode* root, stack<pair<TreeNode*, bool>>& nodes) {
      while (root) {
          nodes.emplace(root, root -> right == nullptr);
          root = root -> left;
      }
}
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> res;
        if (!root) 
            return res;
        stack<pair<TreeNode*, bool>> nodes;
        pushLeft(root, nodes);
        while (!nodes.empty()) {
            if (!nodes.top().second) {
                nodes.top().second = true;
                pushLeft(nodes.top().first -> right, nodes);
            } else {
                res.emplace_back(nodes.top().first -> val);
                nodes.pop();
            }
        }
        return res;
    }
};

Explanation:

Since we push all the left nodes until the left subtree is empty, the top element nodes.top() in the stack is either guaranteed to have an empty left children, or all nodes in its left subtree has been visited. So we only need to record whether its right children has been visited or not. Once its right subtree has been visited, we can safely visit nodes.top() and pop the top.


Morris Traversal:

This algorithm takes O(n) time complexity and O(1) space complexity to complete inorder traversal and preorder traversal. It reconstruct the tree during traversal and recover it after finished traversal.
Detailed explanation: Morris Inorder Tree Traversal

This algorithm can only be used to solve pre/in order.

class Solution {
public:
    void recoverTree(TreeNode* root) {
        TreeNode* current = root;
        TreeNode* first = NULL;
        TreeNode* second = NULL;
        TreeNode* pre = NULL;
        while (current) {
            if (!current -> left) {
                if (pre && pre -> val > current -> val) {
                    if (!first) {
                        first = pre;
                        second = current;
                    } else {
                        second = current;
                    }
                }
                pre = current;
                current = current -> right;
            } else {
                TreeNode* predecessor = current -> left;
                while (predecessor -> right && predecessor -> right != current)
                    predecessor = predecessor -> right;
                if (predecessor -> right) {
                    if (pre && pre -> val > current -> val) {
                        if (!first) {
                            first = pre;
                            second = current;
                        } else {
                            second = current;
                        }
                    }
                    pre = current;
                    predecessor -> right = nullptr;
                    current = current -> right;
                } else {
                    predecessor -> right = current;
                    current = current -> left;
                }
            }
        }
        swap(first -> val, second -> val);
    }
};

Explanation:

Problem #99 require O(n) time complexity and O(1) space complexity, but need to traverse the whole tree to get those wrong nodes. Recursive algorithm uses system stack which is also counted as occupying memory, therefore, Morris Traversal is qualified here.


上一篇下一篇

猜你喜欢

热点阅读