【leetcode100-036】【链表】二叉树中序遍历(四解)

本文介绍了四种不同的算法来遍历二叉树的中序序列:递归实现、栈迭代(包括优化的着色法)、Morris遍历。作者详细阐述了每种方法的思路和操作过程,强调了经典链表遍历的重要性。
摘要由CSDN通过智能技术生成

【题干】

给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。

【思路】

由于题很简单,今天试试写多解,不然实在是太偷懒了。

  1. 递归:对任意非空节点执行“中序遍历左子树-visit自身-中序遍历右子树”
  2. 栈迭代:我认为本质上其实是模拟递归,或者说把递归的函数调用栈显式地写在了代码里面。对任意非空节点执行“有左子树时入栈并向左下行,无左子树时退栈visit并重新向右下行,无法下行时退栈visit并重新向右下行”。
  3. 栈迭代优化(着色法):从方法二可以看出,对一个有左子树的节点,我们第一次访问时用它拓展左子树,而当我们从它的左子树退出时才会在退栈时第二次访问它,并记录进结果中,对这个二次访问的动作,我们为每个节点添加一个颜色属性,在它第一次入栈时给它上色,而退栈时若遇到有色节点,则代表可以访问,借此简化了判断逻辑。
  4. Morris:如方法三分析,当我们访问完A节点的左子树时,做了两个动作,一个是从左子树上最后被访问的节点pre回到A节点,另一个是通过某种方式确定这是第二次访问到A节点,于是把A记录,Morris方法用一个动作解决了这两件事——把pre的right指针指向A,为什么这么做可以解决两件事呢?首先,回到了A,这不用解释了吧?其次,二次访问,当你找到一个A,发现它的pre已经指向了A,那肯定是之前曾经到过pre啊。而A作为pre的祖先,到过pre必然也到过A,本次显然至少是第二次访问A了,条件达成~当然这个方法在不允许修改原数据的情况下就会失效了......

怎么说呢,链表遍历毕竟还是太经典了,所以只是记录了一下关键思路,让我们来看代码吧。

【题解】

递归

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

迭代

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

着色

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ans;
        stack<pair<TreeNode*,int> > stk;
        stk.push(make_pair(root,0));
        while(!stk.empty()){
            auto [node, color]=stk.top();
            stk.pop();
            if(node==nullptr)continue;
            if(color==0){
                stk.push(make_pair(node->right,0));
                stk.push(make_pair(node,1));
                stk.push(make_pair(node->left,0));
            }else{
                ans.push_back(node->val);
            }
        }
        return ans;
    }
};

Morris

class Solution {
public:
    TreeNode* getPredecessor(TreeNode* root){
        TreeNode* cur=root->left;
        while(cur->right&&cur->right!=root) cur=cur->right;
        return cur;
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ans;
        while(root){
            if(root->left){
                TreeNode* pre=getPredecessor(root);
                if(!pre->right){
                    pre->right=root;
                    root=root->left;
                }else{
                    ans.push_back(root->val);
                    root=root->right;
                    pre->right=nullptr;
                }
            }else{
                ans.push_back(root->val);
                root=root->right;
            }
        }
        return ans;
    }
};

  • 25
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值