zoukankan      html  css  js  c++  java
  • 【二叉树】二叉树遍历总结

      节点定义如下

    1 // Definition for a binary tree node.
    2 struct TreeNode {
    3     int val;
    4     TreeNode *left;
    5     TreeNode *right;
    6     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
    7 }

      前序遍历:

      若二叉树为空,则空操作返回,否则:

    1. 访问根节点
    2. 前序遍历根节点的左子树
    3. 前序遍历根节点的右子树  

      中序遍历:

      若二叉树为空,则空操作返回,否则:

    1. 中序遍历根节点的左子树
    2. 访问根节点
    3. 中序遍历根节点的右子树

      后序遍历:

      若二叉树为空,则空操作返回,否则:

    1. 后序遍历根节点的左子树
    2. 后序遍历根节点的右子树
    3. 访问根节点  

       层序遍历:

      其中后序遍历可以看作是和前序遍历是对称的,所以从根节点开始先遍历右子树,再遍历左子树,不过要把最后的遍历输出翻转过来。所以后序遍历的代码和前序遍历的代码基本一致,只是左右子树的访问顺序不同。

    1. 递归方法

      前序遍历:

    1 void preorderTraversal(TreeNode* root, vector<int>& nums) {
    2     if(!root) return;
    3     nums.push_back(root->val); // 访问根节点
    4     preorderTraversal(root->left); // 前序遍历左子树
    5     preorderTraversal(root->right); // 前序遍历右子树
    6 }

      中序遍历:

    1 void inorderTraversal(TreeNode* root, vector<int>& nums) {
    2     if(!root) return;   
    3     inorderTraversal(root->left, nums); // 中序遍历左子树
    4     nums.push_back(root->val);          // 访问根节点
    5     inorderTraversal(root->right, nums); // 中序遍历右子树
    6 } 

      后序遍历:

    1 void postorderTraversal(TreeNode* root, vector<int>& nums) {
    2     if(!root) return;   
    3     postorderTraversal(root->left, nums); // 后序遍历左子树
    4     postorderTraversal(root->right, nums); // 后序遍历右子树
    5     nums.push_back(root->val);            // 访问根节点
    6 }

        层序遍历:

     1 vector<vector<int>> levelOrder(TreeNode* root) {
     2     vector<vector<int> > res;
     3     levelorder(root, 0, res);
     4     return res;
     5 }
     6 void levelorder(TreeNode *root, int level, vector<vector<int> > &res) {
     7     if (!root) 
     8         return;
     9     if (res.size() == level) 
    10         res.push_back({});
    11     
    12     res[level].push_back(root->val);
    13     
    14     if (root->left) 
    15         levelorder(root->left, level + 1, res);
    16     if (root->right) 
    17         levelorder(root->right, level + 1, res);
    18 }

    2.迭代方法(使用栈)

      

      前序遍历 1):

     1 void preorderTraversal(TreeNode* root, vector<int>& nums) {
     2     TreeNode* cur = root;
     3   stack<TreeNode* > st; 
     4 
     5     while (cur || !st.empty()) {
     6         if (cur) {
     7             st.push(cur);
     8             nums.push_back(cur->val);
     9             cur = cur->left;
    10         } else {
    11             cur = st.top();
    12             st.pop();
    13             cur = cur->right;
    14         }
    15      }
    16 }

      前序遍历 2):

     1 void preorderTraversal(TreeNode* root, vector<int>& nums) {
     2     TreeNode* cur = root;
     3   stack<TreeNode* > st; 
     4 
     5     while (cur || !st.empty()) {
     6         if (!cur) {                           // 根节点为空,则需要返回上一级
     7             cur = st.top();               // 访问右子树,而右子树已被保存在栈中
     8             st.pop();
     9         }            
    10         nums.push_back(cur->val);  // 访问根节点
    11         if (cur->right) st.push(cur->right); // 保存待遍历节点
    12         cur = cur->left; // 继续访问左子树
    13     }             
    14 } 

      前序遍历 3):

    void preorderTraversal(TreeNode* root, vector<int>& nums) {
      stack<TreeNode* > st; 
        if (!root)
            return;
        st.push(root);
    
        while (!st.empty()) {
            TreeNode* cur = st.top();
            st.pop();
            nums.push_back(cur->val);
            if (cur->right)
                st.push(cur->right);
            if (cur->left)
                st.push(cur->left); 
        }             
    }

      中序遍历:

     1 void inorderTraversal(TreeNode* root, vector<int>& nums) {
     2     TreeNode* cur = root;
     3     stack<TreeNode* > st; 
     4 
     5     while (cur || !st.empty()) {
     6         if (cur) {                                 // 找到最左节点并保存根节点
     7             st.push(cur);                     // 即访问左子树
     8             cur = cur->left;
     9         } else {                                  
    10             cur = st.top();
    11             st.pop();
    12             nums.push_back(cur->val);    // 访问根节点
    13             cur = cur->right;                      // 访问右子树
    14         }
    15     }
    16 } 

      后序遍历 1) 遍历结果需要翻转:

     1 void postorderTraversal(TreeNode* root, vector<int>& nums) {
     2     stack<TreeNode* > st;
     3     TreeNode* cur = root;
     4 
     5     while (cur || !st.empty()) {
     6         if (cur) {
     7             nums.push_back(cur->val);
     8             st.push(cur);
     9             cur = cur->right;
    10         } else {
    11             cur = st.top();
    12             st.pop();
    13             cur = cur->left;
    14         }
    15     }
    16     reverse(nums.begin(), nums.end());
    17 }

      后序遍历 2) 遍历结果需要翻转:

     1 void postorderTraversal(TreeNode* root, vector<int>& nums) {
     2     stack<TreeNode* > st;
     3     TreeNode* cur = root;
     4 
     5     while (cur|| !st.empty()) {
     6         if (!cur) {
     7             cur= st.top();
     8             st.pop();
     9         }
    10         nums.push_back(cur->val);
    11         if (cur->left) st.push(cur->left);
    12         cur= cur->right;
    13     }
    14     reverse(nums.begin(), nums.end());
    15 }

      后序遍历 3) 遍历结果无需翻转:

     1 void postorderTraversal(TreeNode* root, vector<int>& nums) {
     2     stack<TreeNode* > st;
     3     TreeNode* cur = root, *pre = nullptr;
     4 
     5     while (cur || !st.empty()) {
     6         while (cur) {          // 找到最左节点,并存储沿路节点
     7             st.push(cur);
     8             cur = cur->left;
     9         }
    10         cur = st.top();        // 
    11         if (!cur->right || cur->right == pre) {  // 右子树已遍历或为空,则访问根节点
    12             nums.push_back(cur->val);           // 并将根节点从栈中推出
    13             pre = cur;
    14             st.pop();
    15             cur = nullptr;        // cur置为null,从而再从栈中取节点,即返回父节点
    16         } else {                                 
    17             cur = cur->right;     // 当前节点的右子树尚未遍历,则该节点保留在栈中
    18         }                         // 遍历右子树
    19     }
    20 }

     3.Morris遍历

      Morris算法基于线段树,利用叶子结点的空指针建立索引,此索引按照中序遍历的顺序建立。

      前序遍历:

      

     1 void preorderTraversal(TreeNode* root, vector<int>& nums) {
     2     TreeNode* cur = root, *pre = nullptr;
     3 
     4     while (cur) {
     5         if (cur->left) {   // 左孩子不为空,则找中序遍历下的前驱结点
     6             pre= cur->left;  // 前驱结点为左子树的最右节点
     7             while (pre->right && pre->right != cur) { // 找到最优节点,且该节点未建立线索
     8                 pre = pre->right; // cur的中序遍历下的前驱结点
     9             }
    10 
    11             if (pre->right == cur) {  // cur与pre已建立线索,则去掉该线索
    12                 pre->right = nullptr;  // 剪掉线索
    13                 cur= cur->right;       // 左子树已遍历完毕,遍历右子树
    14             } else {                    // cur与pre并未建立线索,则建立线索
    15                 nums.push_back(cur->val);
    16                 pre->right = cur;   
    17                 cur= cur->left;    // 遍历左子树,继续建立线索
    18             }
    19         } else {
    20             nums.push_back(cur->val);   
    21             cur = cur->right;  // 左子树为空,则返回父节点,且父节点已与此节点已建立线索,即parent = cur->right
    22         }
    23     }
    24 }

      中序遍历:

     1 void inorderTraversal(TreeNode* root, vector<int>& nums) {
     2     TreeNode* cur = root, *pre = nullptr;
     3 
     4     while (cur) {
     5         if (cur->left) {
     6             pre = cur->left;
     7             while (pre->right && pre->right != cur) {
     8                 pre = pre->right;
     9             }
    10 
    11             if (pre->right == cur) {
    12                 nums.push_back(cur->val); // 建立的线索是根据中序遍历
    13                 pre->right = nullptr;
    14                 cur = cur->right;
    15             } else {
    16                 pre->right = cur;
    17                 cur = cur->left;
    18             }               
    19         } else {
    20             nums.push_back(cur->val);
    21             cur = cur->right;
    22         }
    23     }
    24 } 

      后序遍历:

     1 void postorderTraversal(TreeNode* root, vector<int>& nums) {
     2     TreeNode* cur = root, *pre = nullptr;
     3 
     4     while (cur) {
     5         if (cur->right) {
     6             pre = cur->right;
     7             while (pre->left && pre->left != cur) {
     8                 pre = pre->left;
     9             }
    10 
    11             if (pre->left == cur) {
    12                 pre->left = nullptr;
    13                 cur = cur->left;
    14             } else {
    15                 nums.push_back(cur->val);
    16                 pre->left = cur;
    17                 cur = cur->right;
    18             }
    19 
    20         } else {
    21             nums.push_back(cur->val);
    22             cur = cur->left;
    23         }
    24     }
    25     reverse(nums.begin(), nums.end());
    26 } 

    4.分治

      前序遍历:

     1 vector<int> preorderTraversal(TreeNode* root) {
     2     vector<int> res;
     3     if (!root)
     4         return res;
     5         
     6     vector<int> leftSubTree = preorderTraversal(root->left);
     7     vector<int> rightSubTree = preorderTraversal(root->right);
     8 
     9     res.push_back(root->val);
    10     res.insert(res.end(), leftSubTree.begin(), leftSubTree.end());
    11     res.insert(res.end(), rightSubTree.begin(), rightSubTree.end());
    12 
    13     return res;       
    14 }
  • 相关阅读:
    K8S之traefik高级特性
    docker nginx-php容器镜像瘦身优化
    Dubbo-服务消费者初始化
    ThreadLocal 源码分析
    JVM 对象分配规则
    JVM GC 机制
    LockSupport
    自旋锁
    CAS
    Synchronized 监视器锁
  • 原文地址:https://www.cnblogs.com/Atanisi/p/8587303.html
Copyright © 2011-2022 走看看