Binary Tree


二叉树相关问题常常能用recursion解决,而且往往非常简单。要非常熟练。对应的iteration方法也要掌握。

Anytime when you found that doing top down approach uses a lot of repeated calculation, bottom up approach usually is able to be more efficient.

top-down,bottom-up, divide-conquer 常用思路

http://www.cnblogs.com/AnnieKim/archive/2013/06/15/morristraversal.html

1. Preorder traversal


1. Recersion

void preorderHelper(TreeNode *root, vector<TreeNode*> &nodelist) {
        if(root == NULL) return;
        nodelist.push_back(root);
        preorder(root->left, nodelist);
        preorder(root->right, nodelist);
    }

1.2 Iteration

将根结点入栈; 每次从栈顶弹出一个结点,访问该结点; 把当前结点的右孩子入栈; 把当前结点的左孩子入栈。

void preorder(TreeNode *root, vector<TreeNode*> &nodelist) {
        if(root == NULL) return;
        stack<TreeNode*> mystk;
        mystk.push(root);
        while(!mystk.empty()) {
            TreeNode *cur = mystk.top();
            mystk.pop();
            nodelist.push_back(cur);
            if(cur->right) {
                mystk.push(cur->right);
            }
            if(cur->left) {
                mystk.push(cur->left);
            }
        }
    }

1.3 Morris Traversal

步骤:

  1. 如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。

  2. 如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。

    a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。输出当前节点(在这里输出,这是与中序遍历唯一一点不同)。当前节点更新为当前节点的左孩子。

    b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空。当前节点更新为当前节点的右孩子。

  3. 重复以上1、2直到当前节点为空。

void preorder(TreeNode *root, vector<TreeNode*> &nodelist) {
        if(root == NULL) return;
        TreeNode *cur = root;
        TreeNode *prev = nullptr;
        while(cur) {
            if(cur->left == nullptr) {//step 1
                nodelist.push_back(cur);//output
                cur = cur->right;
            } else {//step 2
                prev = cur->left;
                while(prev->right != nullptr && prev->right != cur) {
                    prev = prev->right;//find previous in in-order tranvesal
                }
                if(prev->right == nullptr) {//step 2.1
                    nodelist.push_back(cur);//ouput
                    prev->right = cur;
                    cur = cur->left;
                } else {//prev->right == cur step 2.2
                    cur = cur->right;
                    prev->right = nullptr;
                }
            }
        }
    }

2. Inorder Traversal


2.1 Recrusion

void inorderTraversalHelper(TreeNode *root, vector<int> &nodelist) {
        if(root == NULL) return;
        inorderTraversalHelper(root->left, nodelist);
        nodelist.push_back(root->val);
        inorderTraversalHelper(root->right, nodelist);
    }

2.2 Itereation

  1. 初始化一个二叉树结点pNode指向根结点;
  2. 若pNode非空,那么就把pNode入栈,并把pNode变为其左孩子;(直到最左边的结点)
  3. 若pNode为空,弹出栈顶的结点,并访问该结点,将pNode指向其右孩子(访问最左边的结点,并遍历其右子树)
  4. 重复2和3直到pNode和stack都为空
    vector<int> inorderTraversal(TreeNode *root) {
         // write your code here
         vector<int> result;
         TreeNode *cur = root;
         stack<TreeNode*> mystk;
         while(cur != NULL || !mystk.empty()) {
             if(cur != NULL) {
                 mystk.push(cur);
                 cur = cur->left;
             } else {
                 cur = mystk.top();
                 mystk.pop();
                 result.push_back(cur->val);
                 cur = cur->right;
             }
         }
         return result;
     }
    

2.3 Morris Traversal

  1. 如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。

  2. 如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。

    a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。当前节点更新为当前节点的左孩子。跟前序不同,此时不输出。

    b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空(恢复树的形状)。输出当前节点。当前节点更新为当前节点的右孩子。

  3. 重复以上1、2直到当前节点为空。

vector<int> inorderTraversal(TreeNode *root) {
        // write your code here
        vector<int> result;
        TreeNode *cur = root;
        TreeNode *prev = nullptr;
        while(cur) {
            if(cur->left == nullptr) {
                result.push_back(cur->val);
                cur = cur->right;
            } else {
                prev = cur->left;
                while(prev->right != nullptr && prev->right != cur) {
                    prev = prev->right;
                }
                if(prev->right == nullptr) {
                    prev->right = cur;
                    cur = cur->left;
                } else {
                    result.push_back(cur->val);
                    prev->right = nullptr;
                    cur = cur->right;
                }
            }
        }
        return result;
    }

3. Postorder Traversal

3.1 Recursion

void postorder(TreeNode *root, vector<int> &result) {
        if(root == NULL)
            return;
        postorder(root->left, result);
        postorder(root->right, result);
        result.push_back(root->val);
    }

3.2 Iteration

class Solution {
    /**
     * @param root: The root of binary tree.
     * @return: Postorder in vector which contains node values.
     */
public:
    vector<int> postorderTraversal(TreeNode *root) {
        // write your code here
        stack<TreeNode*> mystk;
        vector<int> result;
        if(root == NULL) 
            return result;
        mystk.push(root);
        TreeNode*cur, *pre;
        cur = root;
        pre = root;
        while (!mystk.empty()) {
            cur = mystk.top();
            if((cur->left == NULL && cur->right == NULL) || cur->right==pre
                || cur->left==pre) {
                mystk.pop();
                result.push_back(cur->val);
                pre = cur;
            } else {
                if(cur->right)
                    mystk.push(cur->right);
                if(cur->left)
                    mystk.push(cur->left);
            }
        }
    }
};

3.3 Morrise traversal O(1) space

当前节点设置为临时节点dump。令其左孩子是root

  1. 如果当前节点的左孩子为空,则将其右孩子作为当前节点。

  2. 如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。

    a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。当前节点更新为当前节点的左孩子。

    b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空。倒序输出从当前节点的左孩子到该前驱节点这条路径上的所有节点。当前节点更新为当前节点的右孩子。

  3. 重复以上1、2直到当前节点为空。

class Solution {
    /**
     * @param root: The root of binary tree.
     * @return: Postorder in vector which contains node values.
     */
public:
    vector<int> postorderTraversal(TreeNode *root) {
        // write your code here
         vector<int> res;
        TreeNode dummy(INT_MIN);
        dummy.left = root;
        TreeNode *curr = &dummy;
        while (curr) {
            if (!curr->left) {
                curr = curr->right;
            } else {
                TreeNode *node = curr->left;
                while (node->right && node->right != curr) {
                    node = node->right;
                }
                if (!node->right) {
                    node->right = curr;
                    curr = curr->left;
                } else {
                    vector<int> v = trace_back(curr->left, node);
                    res.insert(res.end(), v.begin(), v.end());
                    node->right = nullptr;
                    curr = curr->right;
                }
            }
        }
        return res;
    }

    vector<int> trace_back(TreeNode *frm, TreeNode *to) {
        vector<int> res;
        TreeNode *curr = frm;
        while (curr != to) {
            res.emplace_back(curr->val);
            curr = curr->right;
        }
        res.emplace_back(to->val);
        reverse(res.begin(), res.end());
        return res;
    }
};

Follow UP. 上面trace_back函数返回一个vector,然后reverse it, insert at the end of result. vector的构建、revers、insert都要耗费时间,把from到to的nodes看成一个list,那就是链表反转问题了,反转以后直接emplace到result尾部,省去了专门的反转和insert,节省copy and move的时间。

void OutputFromTo(TreeNode *from, TreeNode *to, vector<int> &nodes) {
        TreeNode *cur = from;
        TreeNode dummy(INT_MIN);
        dummy.right = cur;
        TreeNode *prev = &dummy;
        while(cur != to) {//reverse the node list "from =----- to"
            TreeNode *tmp = cur->right;
            cur->right = prev;
            prev = cur;
            cur = tmp;
        }
        cur->right = prev;
        while(cur != &dummy) {//output
            nodes.push_back(cur->val);
            cur = cur->right;
        }
    }

results matching ""

    No results matching ""