当前位置:   article > 正文

前中后序遍历(递归法和迭代法)_后序遍历递归算法0xfffffffffffffff7

后序遍历递归算法0xfffffffffffffff7

解决三个题目:前中后序的遍历
二叉树的前序遍历
题目:
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

二叉树的后序遍历
题目:
给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 。

二叉树的中序遍历
题目:
给定一个二叉树的根节点 root ,返回它的 中序 遍历。
代码随想录

1:确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。

2:确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。

3:确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显式地将这个栈模拟出来。
用栈的要点在于模拟出整个过程然后再来写代码。
前序递归遍历:
首先根节点,然后遍历左子树,再遍历右子树。

class Solution {
public:
    void Traversal(TreeNode* cur, vector<int> &vec){
        if(cur == nullptr) return ;
        vec.push_back(cur->val);
        Traversal(cur->left, vec);
        Traversal(cur->right, vec);
    }
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> v;
        Traversal(root, v);
        return v;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

①前序迭代遍历:

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        if (root == nullptr) {
            return res;
        }

        stack<TreeNode*> stk;
        TreeNode* node = root;
        while (!stk.empty() || node != nullptr) {
            while (node != nullptr) {
                res.emplace_back(node->val);
                stk.emplace(node);
                node = node->left;
            }
            node = stk.top();
            stk.pop();
            node = node->right;
        }
        return res;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

①前序迭代遍历过程:入根然后一直入左,直到没有左,然后出栈顶(找到最左的节点),再然后找到最左的节点的右孩子,此时右孩子为根节点。然后循环操作。
要点:根节点、左节点处理完之后,把右节点当做根节点然后又从循环开头开始操作(即整理整个右子树)。

②前序迭代遍历:

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> result;
        if (root == NULL) return result;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();                       // 中
            st.pop();
            result.push_back(node->val);
            if (node->right) st.push(node->right);           // 右(空节点不入栈)
            if (node->left) st.push(node->left);             // 左(空节点不入栈)
        }
        return result;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

②前序迭代遍历过程:前序遍历是根左右,首先保存根节点,然后出栈,然后将值入vector。然后入右节点、入左节点再重新进行循环,即将左节点当做根节点进行操作(即操作左子树),操作完左子树之后再操作右子树。
将左节点当做根节点进行操作的要点在于:节点设置为出栈元素。
代码重点:要访问的元素和要处理的元素顺序是一致的,所以只需要按顺序操作每一个节点即可。

首先在脑海中(脑海中树的形状是)模拟出整个过程,此时节点要不要入vector要不要入stack。

后序递归遍历:

class Solution {
public:
    void Traversal(TreeNode* cur, vector<int>& v){
        if(cur == nullptr) return ;
        Traversal(cur->left, v);
        Traversal(cur->right, v);
        v.push_back(cur->val);
    }
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> v;
        Traversal(root, v);
        return v;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

①后序迭代遍历

class Solution {
public:
    vector<int> postorderTraversal(TreeNode *root) {
        vector<int> res;
        if (root == nullptr) {
            return res;
        }

        stack<TreeNode *> stk;
        TreeNode *prev = nullptr;
        while (root != nullptr || !stk.empty()) {
            while (root != nullptr) {
                stk.emplace(root);
                root = root->left;
            }
            root = stk.top();
            stk.pop();
            if (root->right == nullptr || root->right == prev) {
                res.emplace_back(root->val);
                prev = root;
                root = nullptr;
            } else {
                stk.emplace(root);
                root = root->right;
            }
        }
        return res;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

①后续迭代遍历过程:中左一直入栈,直到没有左边,然后查找栈顶节点是否有右节点,没有则出栈入vector,有则将右节点作为根节点重新循环(即将右边那部分直接当做一棵树)。

②后续迭代遍历

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> result;
        if (root == NULL) return result;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node->val);
            if (node->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
            if (node->right) st.push(node->right); // 空节点不入栈
        }
        reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
        return result;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

②后续迭代遍历过程:先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了
中序递归遍历:

class Solution {
public:
    void Traversal(vector<int> & v, TreeNode* cur){
        if(cur == nullptr) return ;
        Traversal(v, cur->left);
        v.push_back(cur->val);
        Traversal(v, cur->right);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> v;
        Traversal(v, root);
        return v;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

①中序迭代遍历

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
       
     vector<int> v;
     stack<TreeNode*>stk;
     TreeNode *node = root;
     while (!stk.empty() || node != nullptr){
         while (node != nullptr){
             stk.emplace(node);
             node = node->left;
         }
         node = stk.top();
         v.emplace_back(node->val);
         stk.pop();
         node = node->right;
     }
     return v;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

中序迭代遍历过程:中左一直入栈,直到没有左边,然后出栈入vector找到右节点然后右节点当做根节点重新循环(即将右边那部分直接当做一棵树)。

②中序迭代遍历

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while (cur != NULL || !st.empty()) {
            if (cur != NULL) { // 指针来访问节点,访问到最底层
                st.push(cur); // 将访问的节点放进栈
                cur = cur->left;                // 左
            } else {
                cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
                st.pop();
                result.push_back(cur->val);     // 中
                cur = cur->right;               // 右
            }
        }
        return result;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

这两段代码其实是没有区别的就是单纯的将 while 换成了 if else。

处理顺序和访问顺序是不一致的。那么在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/274882
推荐阅读
相关标签
  

闽ICP备14008679号