代码随想录训练营Day 14|二叉树的递归遍历、迭代遍历、统一迭代

1.递归遍历

题目链接/文章讲解/视频讲解: 代码随想录

代码:

class Solution {
public:
    void Traversal(TreeNode* cur,vector<int>& result){ 
       // 如果将递归函数中的参数 vector<int>& result 改为 vector<int> result,会导致在递归调用的过程中创建了 result 的副本,而不是对同一个 result 的引用。这意味着在递归调用结束后,每次递归调用中的 result 都会被销毁,因此最终返回的结果将是空的。
        if(cur == NULL){
            return;
        }
        result.push_back(cur->val);
        Traversal(cur->left,result);
        Traversal(cur->right,result);
    }
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        Traversal(root,result);
        return result;
    }
};

代码:

class Solution {
public:
    void Traversal(TreeNode* cur,vector<int>& result){
        if(cur == nullptr){
            return;
        }
        Traversal(cur->left,result);
        Traversal(cur->right,result);
        result.push_back(cur->val);
    }
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        Traversal(root,result);
        return result;
    }
};

代码:

class Solution {
public:
    void traversal(TreeNode* cur,vector<int>& result){
      if(cur == nullptr){
        return;
      }
      traversal(cur->left,result);
      result.push_back(cur->val);
      traversal(cur->right,result);
    }
    vector<int> inorderTraversal(TreeNode* root) {
       vector<int> result;
       traversal(root,result);
       return result;
    }
};

思路:

        递归三部曲:确定递归参数和返回值,确定递归终止条件,确定递归处理逻辑。 

2.二叉树的迭代遍历

题目链接/文章讲解/视频讲解: 代码随想录

前序:

代码:

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

        st.push(root);
        
        while(!st.empty()){
            // 中
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node->val);
            // 右
            if(node->right != nullptr) st.push(node->right);
            // 左
            if(node->left != nullptr) st.push(node->left);
        }
        return result;
    }
};

思路:

用迭代来模拟递归过程离不开栈。 为了实现前序遍历,即中左右,我们先处理中间结点,然后将其右孩子压入栈,再将其左孩子压入栈(考虑到栈先进后出)。

整个代码实现了空结点不入栈。

后序:

代码:

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;

        if(root == nullptr) return result; // 空结点不入栈

        st.push(root);

        while(!st.empty()){
            // 中
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node->val);

            // 左
            if(node->left != nullptr) st.push(node->left);
            // 右
            if(node->right != nullptr) st.push(node->right);
        }
        reverse(result.begin(),result.end());
        return result;
    }
};

思路:

刚刚实现了前序遍历,即中左右。那么我们只要在此基础上,将左右反一下 ,得到中右左。最后将数组翻转,就可以得到后序遍历,左右中。

中序:

代码:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;

        if(root == nullptr) return result;

        TreeNode* cur = root; // 用cur指针来访问结点,用它的值来判断什么时候该处理结点了

        while(cur != nullptr || !st.empty()){
            // 只要存在左孩子,就将左孩子压入栈中
            if(cur != nullptr){
                st.push(cur); // 左
                cur = cur->left;
            }else{ // 没有左孩子,按照栈的顺序处理结点,并将它的右孩子压入栈
                cur = st.top();
                st.pop();      // 中
                result.push_back(cur->val);
                                // 右
                cur = cur->right;
            }
        }
        return result;
    }
};

思路:

因为中序遍历里的访问顺序和处理的顺序是不一样的(所以不能用上一种解法调换顺序就可以达到)。所以我们借助了cur指针来遍历左节点,直到左孩子为空,再开始处理该节点,并在之后将右节点压入栈。

这样还要想着访问顺序和处理顺序也太麻烦了!!有没有那种和递归法一样,只要调换下顺序就都能用的!! 统一迭代来了!!

3.二叉树的统一迭代(标记法)

题目链接/文章讲解: 代码随想录

前序:

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
         vector<int> result;
        stack<TreeNode*> st;
        if(root != NULL) st.push(root);

        while(!st.empty()){
            TreeNode* cur = st.top();
            if(cur != NULL){ // 将访问过的结点按照右左中的顺序压入栈中(栈先进后出)
                st.pop(); // 将中结点弹出,换成右左中

                // 右(空结点不入栈)
                if(cur->right != NULL) st.push(cur->right);
                // 左(空结点不入栈)
                if(cur->left != NULL) st.push(cur->left);
                // 中
                st.push(cur);
                st.push(NULL); // 标记为:“要处理的结点”(没有标记的还需继续去访问它的左右结点,看看有没有遗漏的)
            }else{ // 处理 标记为“要处理的结点”
                st.pop(); // 先将空结点(或标记)弹出
                cur = st.top();
                st.pop();
                result.push_back(cur->val);
            }
        }
        return result;
    }
};

后序:

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        if(root != NULL) st.push(root);

        while(!st.empty()){
            TreeNode* cur = st.top();
            if(cur != NULL){ // 将访问过的结点按照中右左的顺序压入栈中(栈先进后出)
                st.pop(); // 将中结点弹出,换成中右左

                // 中
                st.push(cur);
                st.push(NULL); // 标记为:“要处理的结点”(没有标记的还需继续去访问它的左右结点,看看有没有遗漏的)

                // 右(空结点不入栈)
                if(cur->right != NULL) st.push(cur->right);
               
                // 左(空结点不入栈)
                if(cur->left != NULL) st.push(cur->left);
            }else{ // 处理 标记为“要处理的结点”
                st.pop(); // 先将空结点(或标记)弹出
                cur = st.top();
                st.pop();
                result.push_back(cur->val);
            }
        }
        return result;
    }
};

中序:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
         vector<int> result;
        stack<TreeNode*> st;
        if(root != NULL) st.push(root);

        while(!st.empty()){
            TreeNode* cur = st.top();
            if(cur != NULL){ // 将访问过的结点按照右左中的顺序压入栈中(栈先进后出)
                st.pop(); // 将中结点弹出,换成右左中

                // 右(空结点不入栈)
                if(cur->right != NULL) st.push(cur->right);

                // 中
                st.push(cur);
                st.push(NULL); // 标记为:“要处理的结点”(没有标记的还需继续去访问它的左右结点,看看有没有遗漏的)
               
                // 左(空结点不入栈)
                if(cur->left != NULL) st.push(cur->left);
            }else{ // 处理 标记为“要处理的结点”
                st.pop(); // 先将空结点(或标记)弹出
                cur = st.top();
                st.pop();
                result.push_back(cur->val);
            }
        }
        return result;
    }
};

思路:

使用栈的话,无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况

那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。

如何标记呢,就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 这种方法也可以叫做标记法。

遇到NULL就处理结点;遇到不是NULL的情况,就 访问结点(访问顺序和要求的遍历顺序相反,因为栈先进后出)

这种思路还是得自己画图模拟一遍,思路难,代码还是好写的。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值