Leetcode:Binary Tree Traversal 二叉树遍历总结

首先二叉树的数据结构表示:

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

 1. 先序遍历(递归版本)

 2. 中序遍历(递归版本)

 3. 后序遍历(递归版本)

 4. 先序遍历(非递归版本)

 5. 中序遍历(非递归版本)

 6. 后序遍历(非递归版本)

 7. 中序遍历(非递归版本,不用栈, Morris遍历)

 8. 先序遍历(非递归版本,不用栈, Morris遍历)

 9. 层次遍历(递归版本)

10. 层次遍历(非递归版本)

 

 

 

 

1. 先序遍历(递归版本)

class Solution {
public:
    vector<int> preorderTraversal(TreeNode *root) {
        vector<int> res;
        preorder(root, res);
        return res;
    }
    void preorder(TreeNode* root, vector<int>& res) {
        if (root == NULL) return;
        if (root != NULL) {
            res.push_back(root->val);
            preorder(root->left, res);
            preorder(root->right, res);
        }
    }
};

2. 中序遍历(递归版本)

class Solution {
public:
    vector<int> inorderTraversal(TreeNode *root) {
        vector<int> res;
        inorder(root, res);
        return res;
 
    }
    void inorder(TreeNode* root, vector<int>& res) {
        if (root == NULL) return;
        if (root != NULL) {
            inorder(root->left, res);
            res.push_back(root->val);
            inorder(root->right, res);
        }
    } 
};

3.后序遍历(递归版本)

class Solution {
public:
    vector<int> postorderTraversal(TreeNode *root) {
        vector<int> res;
        postorder(root, res);
        return res;
    }
    
    void postorder(TreeNode* root, vector<int>& res) {
        if (root == NULL) return;
        if (root != NULL) {
            postorder(root->left, res);
            postorder(root->right, res);
            res.push_back(root->val);
        }
    }
};

 

4. 先序遍历(非递归版本)

class Solution {
public:
    vector<int> preorderTraversal(TreeNode *root) {
          vector<int> res;
          stack<TreeNode*> stk;
          while (!stk.empty() || root != NULL) {
              if (root != NULL) {  
                  res.push_back(root->val);  
                  stk.push(root);
                  root = root->left;
              } else {
                  TreeNode* p = stk.top();
                  stk.pop();
                  root = p->right;
              }
          }
          return res;
    }
};

 

5. 中序遍历(非递归版本)

class Solution {
public:
    vector<int> inorderTraversal(TreeNode *root) {
        vector<int> res;
        stack<TreeNode*> stk;
        while (!stk.empty() || root != NULL) {
            if (root != NULL) {
                stk.push(root);
                root = root->left;
            } else {
                TreeNode* p = stk.top();
                stk.pop();
                res.push_back(p->val);
                root = p->right;
            }
        }
        return res;
    } 
};

 6. 后序遍历(非递归版本)

这里讲解一下,非递归遍历的时候,我们最需要关注的根节点的入栈情况。

6.1 在先序遍历中,因为是 根左右 的顺序,所以每当遍历到根节点时,我们首先就访问它并且将它入栈

 

6.2 在中序遍历中,因为是 左右根 的顺序,所以每当遍历到某节点时,我们只能将它入栈,并不能立即访问它,必须等它的所有左子树节点都访问完之后才能访问该节点。

什么时候才是左子树节点全部访问完呢?  

我们可以注意到: 每次入栈节点之后,接下来操作的都是其左儿子,也就是说 入栈顺序是:当前节点-->左儿子-->左孙子...

因为栈的后入先出的特性,我们可以推出 当从栈中弹出的是 当前节点的时候,那么其左孙子、左儿子..一定已经依次出栈了并且被访问了,这个时候我们就可以访问该当前节点了

 

6.3 在后序遍历中,因为是 左右根 的顺序,所以每当遍历到某节点时,我们只能将它入栈,并不能立即访问它,

必须等它的所有左子树节点 和 它的所有右子树节点 都访问完之后才能访问该节点。

什么时候才是其左子树节点和右子树节点全部访问完呢?

我们可以注意到: 每次入栈节点之后,接下来操作的都是其左儿子,也就是说 入栈顺序是:当前节点-->左儿子-->左孙子...

因为栈的后入先出的特性,我们可以推出 当从栈中弹出的是 当前节点的时候,那么其左孙子、左儿子..一定已经依次出栈了并且被访问了

但是,这个时候我们还不能访问该当前节点了!!! 因为右子树节点都没有访问...

这个时候,该节点的右子树节点入栈,当该节点第二次出栈(第一次应该要出栈,但是不能出栈)的时候才能访问。

 

我们换个思路,后序遍历是 左右根,为什么不能像先序或者中序一样简单呢? 最根本的原因就在于 根节点是最后被访问

我们反转一下,将 左右根 反转为 根右左, 然后将结果反转就可以得到最终结果了,这里我们先转化为 根右左 是因为这个根节点最先访问,非常简单。

怎样进行 根右左 遍历? 这个就简单了,只需要将 先序遍历的左右互换就可以了。

class Solution {
public:
    vector<int> postorderTraversal(TreeNode *root) {
          vector<int> res;
          stack<TreeNode*> stk;
          while (!stk.empty() || root != NULL) {
              if (root != NULL) {
                  res.push_back(root->val);
                  stk.push(root);
                  root = root->right;
              } else {
                  TreeNode* p = stk.top();
                  stk.pop();
                  root = p->left;
              }
          }
          reverse(res.begin(), res.end());
          return res;
    }
};

 

想了想,这样转化的方法不是很优雅,这样只是AC了这道题,但是没有按照 后序遍历的本质去解答

访问不只是 打印输出,如果要对每个节点干点别的事呢? 这时这个办法就没用了。看来还是得 按照 后序遍历的思路来,只是稍微麻烦些。

class Solution {
public:
    vector<int> postorderTraversal(TreeNode *root) {
        vector<int> res;
        if (root == NULL) return res;
        stack<TreeNode*> stk;
        
        TreeNode* cur = root;   // cur为正在访问的结点
        TreeNode* prev = NULL;  // prev为刚刚访问过的结点
        
        do {
            while (cur != NULL) {  // 一直往左下走
                stk.push(cur);
                cur = cur->left;
            }
            
            prev = NULL;
            while (!stk.empty()) {
                cur = stk.top();
                stk.pop();
                if (cur->right == prev) {   // 右孩子不存在或已被访问,访问之
                    res.push_back(cur->val);
                    prev = cur;             // 更新刚访问的结点
                } else {
                    stk.push(cur);          // 当前结点不能访问,需第二次入栈
                    cur = cur->right;       // 先处理右子树
                    break;                  // 断开重新循环
                }
            }
        } while (!stk.empty());

        return res;
    }
};

 

 7. 中序遍历(非递归版本,不用栈, Morris遍历)

Morris遍历是基于线索二叉树的。

简单描述一下,二叉树中有一些度为1的节点和叶子节点,我们可以利用这些空缺的位置来进行线索化。

每当一个节点的左儿子为空时,我们将其左儿子指针指向该节点的前驱prev

每当一个节点的右儿子为空时,我们将其右儿子指针指向该节点的后继next

注意:前驱和后继这两个概念是基于中序遍历结果序列中的相对位置的

一个节点的前驱prev:该节点的左子树最右下节点(不一定为叶子节点,但其右儿子一定为空)

一个节点的后继next:该节点的右子树最左下节点(不一定为叶子节点,但其左儿子一定为空)

注意:线索化二叉树的过程中会修改原来二叉树的形态,我们在中序遍历的过程中,有个  修改右儿子(线索化,修改二叉树) 和 清零右儿子(恢复二叉树原态) 相对过程

class Solution {
public:
    vector<int> inorderTraversal(TreeNode *root) {
        vector<int> res;
        TreeNode* curr = root;
        TreeNode* prev = NULL;
        while (curr != NULL) {
            if (curr->left == NULL) {
                res.push_back(curr->val);
                curr = curr->right;
            } else {
                prev = curr->left;
                while (prev->right != NULL && prev->right != curr) {
                    prev = prev->right;          // find the prev node of curr
                }
                if (prev->right == NULL) {
                    prev->right = curr;         //  threading. modify the tree
                    curr = curr->left;
                } else {
                    prev->right = NULL;         //  clear thread. recover the tree
                    res.push_back(curr->val);
                    curr = curr->right;
                }
            }
        }
        return res;
    } 
};

 

8. 先序遍历(非递归版本,不用栈, Morris遍历)

class Solution {
public:
    vector<int> preorderTraversal(TreeNode *root) {
        vector<int> res;
        TreeNode* curr = root;
        TreeNode* prev = NULL;
        while (curr != NULL) {
            if (curr->left == NULL) {
                res.push_back(curr->val);
                curr = curr->right;
            } else {
                prev = curr->left;
                while (prev->right != NULL && prev->right != curr) {
                    prev = prev->right;          // find the prev node of curr
                }
                if (prev->right == NULL) {
                    res.push_back(curr->val);
                    prev->right = curr;         //  threading. modify the tree
                    curr = curr->left;
                } else {
                    prev->right = NULL;         //  clear thread. recover the tree
                    curr = curr->right;
                }
            }
        }
        return res;
    }
};

 

9. 层次遍历(递归版本)

Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level).

For example:
Given binary tree {3,9,20,#,#,15,7},

    3
   / \
  9  20
    /  \
   15   7

 

return its level order traversal as:

[
  [3],
  [9,20],
  [15,7]
]

 

 
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int> > levelOrder(TreeNode *root) {
        vector<vector<int>> result;
        traverse(root, 1, result);
        return result;
    }
    void traverse(TreeNode* root, int level, vector<vector<int>>& result) {
        if (root == nullptr) return;
        if (level > result.size()) {
            result.push_back(vector<int>());
        }
        result.at(level-1).push_back(root->val);
        traverse(root->left, level + 1, result);
        traverse(root->right, level + 1, result);
    }
};

 

10. 层次遍历(非递归版本)

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int> > levelOrder(TreeNode *root) {
        vector<vector<int>> result;
        if (root == nullptr) return result;
        
        queue<TreeNode*> current;
        queue<TreeNode*> next;
        
        current.push(root);
        
        vector<int> level;
        while (!current.empty()) {
            while (!current.empty()) {
                TreeNode* node = current.front();
                current.pop();
                level.push_back(node->val);
                if (node->left != nullptr) next.push(node->left);
                if (node->right != nullptr) next.push(node->right);
            }
            result.push_back(level);
            level.clear();
            swap(current, next);
        }
        return result;
    }
};

 

你可能感兴趣的:(LeetCode)