三种二叉树的中序遍历的方法

题目二叉树的中序遍历

描述
二叉树的中序遍历总所周知遍历顺序为:左子树,根节点,右子树,同时对左右子树仍然采用中序遍历(左子树,根节点,右子树)。

方法1:采用递归,代码比较简单,直接看代码。时间空间复杂度均为O(n),n为树的节点数量。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> ans;
    vector<int> inorderTraversal(TreeNode* root) {
        if (root == nullptr) return ans;
        dfs(root);
        return ans;
    }

    void dfs(TreeNode* root) {
        if (root == nullptr) return;
        if (root->left == nullptr && root->right == nullptr) {//到达叶子节点
            ans.push_back(root->val);
            return;
        }
        dfs(root->left);//访问左子树
        ans.push_back(root->val);//访问根节点
        dfs(root->right);//访问右子树
    }
};

方法2:使用栈,递归本质上就是使用了栈,此时我们手工模拟栈遍历即可。时间空间复杂度和方法1一样,都是O(n)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*> s;
        vector<int> ans;
        while (root || !s.empty()) {
            while (root) {
                s.push(root);
                root = root->left;
            }//以上保证左子树现在为空(null),因为已经到了最左边的一个节点
            root = s.top();
            s.pop();//左子树为null,所以可以访问根节点,将根节点弹出,访问
            ans.push_back(root->val);
            root = root->right;//左子树和根节点都已经访问过,访问右子树
        }
        return ans;
    }
};

方法3:Morris遍历,以上两种方法空间复杂度均为O(n),该方法空间复杂度只需要O(1)

leetcode官方题解对该算法过程描述非常精确,搬运过来(写博客也是方便复习,自己写很难写的比这个出色,不再献丑)
在这里插入图片描述

自己对该算法的理解:Morris遍历利用了这样一个特性,当第一次访问到节点x时,首先查看x节点左子树中最右边的节点,假设该节点为pre,因为pre是左子树最右边的节点,所以pre的右孩子此时一定为空,Morris算法的关键就算利用了pre->right,第一次访问x时将为空的pre->right指向x,x=x->left,一直这样向下,直到到达叶子节点,访问叶子节点,然后访问右子树x=x->right(注意叶子节点x就是pre节点)。

当遍历完x的左子树再一次访问到x时,此时pre->right不为空,通过pre->right不为空可以知道此时已经遍历完左子树,直接遍历根节点x即可,然后遍历右子树x=x->right,同时将pre->right复原置为null。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        while (root) {
            if (root->left) {//当前节点有左子树
                TreeNode *pre = root->left;//找到当前节点的先驱
                while (pre->right && pre->right != root) pre = pre->right;
                
                if (pre->right == nullptr) {//先驱节点右子树为空,
                    pre->right = root;//先驱节点右孩子修改为根节点,做标记
                    root = root->left;//向左遍历
                }
                else {//先驱右孩子不空,这是上次遍历时的标记,说明此时已经访问完左子树
                    res.push_back(root->val);//访问根节点
                    pre->right = nullptr;
                    root = root->right;//访问右子树
                }
            }
            else {//当前节点没有左子树,此时到达最左边,可以认为左子树访问完毕
                res.push_back(root->val);//访问根节点
                root = root->right;//访问右子树
            }
        }
        return res;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值