二叉树的中序遍历问题的具体思路 递归法 迭代法 莫里斯算法(附完整代码)


前言

`
本文将主要围绕以下问题进行讨论:

二叉树的中序遍历。

本文将分别以三种方法【递归】【迭代】【莫里斯算法】来解决这一问题(文章末尾附完整代码)


一、如何理解中序遍历?

二叉树的中序遍历是一种常见的遍历方式,主要按照 “左子树 - 根节点 - 右子树” 的顺序访问二叉树的每个节点。

如下:

	 4
    /  \
   2    6
 / \   /  \
1   3 5    7

首先访问左子树,对于节点 4,先访问其左子树中的节点 2。对于节点 2,再访问其左子树中的节点 1。节点 1 没有左子树,将其值 1 加入列表。然后访问节点 2,将其值 2 加入列表。接着访问节点 2 的右子树中的节点 3,将其值 3 加入列表。

再访问根节点,此时访问节点 4,将其值 4 加入列表。

最后访问右子树,对于节点 4 的右子树中的节点 6。先访问节点 6 的左子树中的节点 5,将其值 5 加入列表。然后访问节点 6,将其值 6 加入列表。接着访问节点 6 的右子树中的节点 7,将其值 7 加入列表。

请添加图片描述

题目链接: [点击跳转] Leetcode 94. 二叉树的中序遍历


二、方法一(递归法)

递归法通过深度优先搜索,将二叉树的所有节点全部按照中序的方式遍历出来。

根据示例所给出的输出格式,我们需要定义一个vector类型的变量,并且在最后输出这个变量。同时,我们还应注意到,如果将空节点传入递归函数这种,应该返回一个空值。

vector<int> tre;
void preOrder(TreeNode *root) {
    if (root == nullptr) {
        return;
    }
}

这个时候,我们就可以根据前序的“左-根-右”原则进行排序和递归啦!

这个递归函数不需要返回值,因为我们最后输出内容为tre这个向量,而函数只需要去递归遍历所有节点。

    vector<int> tre;
    void inorder(TreeNode* root) {
        if (root==nullptr) {
            return;
        }
        inorder(root->left);
        tre.push_back(root->val);
        inorder(root->right);
    }

注意:这里的 if 判断中 return 后空着即可。

完整代码如下:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> tre;
    void inorder(TreeNode* root) {
        if (root==nullptr) {
            return;
        }
        inorder(root->left);
        tre.push_back(root->val);
        inorder(root->right);
    }
    vector<int> inorderTraversal(TreeNode* root) {

        inorder(root);
        return tre;
    }
};

时间复杂度为O(n)。


三、方法二(迭代法)

将遍历结果存储在一个整数向量tre中,同时定义一个存储二叉树节点指针的栈。

vector<int> tre;
stack<TreeNode*> sta;

首先需要判断输入节点是否为空。如若节点不为空且栈不为空(栈不为空,说明二叉树的节点没有全部处理完),则进入内层循环将当前节点压入栈中,再指向左子节点。

vector<int> tre;
stack<TreeNode*> sta;
vector<int> inorderTraversal(TreeNode* root) {
    while(root!=nullptr||!sta.empty()){
        while(root!=nullptr){
        sta.push(root);
        root=root->left;
    }
    return tre;
}

经过如上处理后,就需要考虑将栈中元素按照中序传入向量tre中(注意:在取栈顶元素后,一定要记得将其出栈)

vector<int> tre;
stack<TreeNode*> sta;
vector<int> inorderTraversal(TreeNode* root) {
    while(root!=nullptr||!sta.empty()){
        while(root!=nullptr){
        sta.push(root);
        root=root->left;
        }
    root=sta.top();
    sta.pop(); 
    tre.push_back(root->val);
    root=root->right;
    }
    return tre;
}

完整代码如下:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> tre;
    stack<TreeNode*> sta;
    vector<int> inorderTraversal(TreeNode* root) {
        while(root!=nullptr||!sta.empty()){
            while(root!=nullptr){
            sta.push(root);
            root=root->left;
        }
        root=sta.top();
        sta.pop(); 
        tre.push_back(root->val);
        root=root->right;
        }
        return tre;
    }
};

时间复杂度为O(n)。


四、方法三(莫里斯算法)

如果既不想用递归,也不愿用辅助栈该如何实现呢?

莫里斯算法利用了二叉树中叶子节点的右指针为空这一特性。由于不需要递归和栈,空间复杂度为O(1)。

其核心思想是在遍历过程中,对于每个节点,如果其左子树不为空,就找到其左子树中的最右节点(即中序遍历下该节点的前驱节点),然后将前驱节点的右指针指向当前节点。

这样在遍历完左子树后,可以通过这个指针回到当前节点。如果前驱节点的右指针已经指向当前节点,说明左子树已经遍历完,此时将前驱节点的右指针置为空,然后访问当前节点,并继续遍历当前节点的右子树。

总之,我们就需要记住:

找到 根节点的 左子树 的 最右节点,然后就这个最右节点指向根节点。

如下:

	 4
    /  \
   2    6
 / \   /  \
1   3 5    7

找到根节点4下的左子树,如下:

  2   
 / \  
1   3 

然后找到其最右节点3。

将3指向根节点4。

然后将根节点设置为2,重复上述步骤。

在把4,2,1为根节点的操作重复后,回溯到节点2,然后找节点2的右子节点3,继续重复步骤。

然后再回溯到节点4,找节点4的右子节点6,然后继续。

完整代码如下:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> tre;
    TreeNode* pre;    
    vector<int> inorderTraversal(TreeNode* root) {
        while (root!= nullptr) {
            if (root->left == nullptr) {
                tre.push_back(root->val);
                root = root->right;
            } else {
                pre = root->left;
                while (pre->right!= nullptr && pre->right!= root) {
                    pre = pre->right;
                }
                if (pre->right == nullptr) {
                    pre->right = root;
                    root = root->left;
                } else {
                    pre->right = nullptr;
                    tre.push_back(root->val);
                    root = root->right;
                }
            }
        }
        return tre;
    }
};

时间复杂度为O(n)。

空间复杂度为O(1)。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值