LeetCode94二叉树的中序遍历

原理

二叉树的中序遍历遵循 “左子树 - 根节点 - 右子树” 的顺序来访问二叉树中的每个节点。其基本原理是利用递归的思想,先递归地遍历根节点的左子树,访问完左子树的所有节点后,再访问根节点本身,最后递归地遍历根节点的右子树,这样就能按照中序遍历的规则依次访问二叉树中的所有节点,并将节点的值存储起来,最终得到中序遍历的结果序列。

步骤

  1. 递归函数 inorder 的执行步骤(inorder 函数部分)
    • 首先判断传入的当前节点指针 root 是否为空:
      • 如果 root 为空(即 root == nullptr),说明已经遍历到了子树的末尾或者本身传入的就是空树,此时直接返回,不需要进行后续操作。
      • 如果 root 不为空,则按照中序遍历的顺序进行操作:
        • 先递归调用 inorder(root->left, res),这一步会进入根节点的左子树,继续按照中序遍历规则访问左子树中的节点,直到左子树的最底层左叶子节点(不断深入左子树进行递归)。
        • 当左子树遍历完成后(即从最底层左叶子节点的递归调用返回后),执行 res.push_back(root->val),将当前根节点的值添加到结果向量 res 中,此时就完成了对当前根节点的访问,符合中序遍历 “左子树 - 根节点 - 右子树” 的顺序要求。
        • 最后递归调用 inorder(root->right, res),进入根节点的右子树,同样按照中序遍历规则继续访问右子树中的节点,如此递归下去,直到整个二叉树的所有节点都被访问到。
  2. 主函数 inorderTraversal 的执行步骤(inorderTraversal 函数部分)
    • 在 inorderTraversal 函数中,首先创建一个空的 vector 类型的变量 res,用于存储二叉树中序遍历的结果(即各个节点的值)。
    • 然后调用 inorder(root, res) 函数,传入二叉树的根节点指针 root 和结果向量 res,开始对整个二叉树进行中序遍历,在 inorder 函数的递归调用过程中,逐步将节点的值添加到 res 中。
    • 最后,当 inorder 函数执行完毕(意味着整个二叉树已经按照中序遍历规则访问完成),返回 res,这个 res 中存储的就是二叉树中序遍历的结果序列。

图示法表示步骤(以一个简单的二叉树示例来说明,如下图所示)

  1. 从根节点开始
    首先传入根节点(值为 1)到 inorder 函数,判断根节点不为空,进入左子树递归调用(以根节点的左子树,根节点值为 2 的子树为例继续说明)。

  2. 遍历左子树(值为 2 的节点的子树)
    对于值为 2 的节点,再次进入其左子树递归调用(以值为 4 的节点为例),值为 4 的节点左子树为空,直接返回,然后将值为 4 的节点的值添加到 res 中,接着进入其右子树(值为 5 的节点),值为 5 的节点左子树也为空,添加值为 5 的节点的值到 res 中,此时值为 2 的节点的左子树遍历完成。

  3. 访问根节点(值为 2 的节点)
    将值为 2 的节点的值添加到 res 中,接着进入其右子树继续递归遍历(这里省略右子树内部类似的详细步骤,原理和左子树一样)。

  4. 回到根节点(值为 1 的节点)
    当值为 2 的节点及其左右子树都遍历完成后,返回上一层(即回到值为 1 的根节点),此时将值为 1 的根节点的值添加到 res 中,然后进入根节点的右子树(值为 3 的节点及其子树),按照同样的递归步骤遍历右子树,直到整个二叉树所有节点都被遍历完。

 

最终中序遍历的结果序列存储在 res 中,按照上述示例二叉树,结果为 [4, 2, 5, 1, 6, 3, 7]

代码关键行注释

class Solution {
public:
    // 中序遍历的递归函数,用于遍历以root为根节点的二叉树,将遍历结果存储到res向量中
    void inorder(TreeNode* root, vector& res) {
        if (root) {  // 如果当前节点不为空
            inorder(root->left, res);  // 先递归遍历左子树
            res.push_back(root->val);  // 将当前根节点的值添加到结果向量res中
            inorder(root->right, res);  // 再递归遍历右子树
        }
        else {  // 如果当前节点为空,直接返回,结束当前递归分支
            return;
        }
    }

    // 对外接口函数,用于启动对整个二叉树的中序遍历,返回中序遍历结果的向量
    vector inorderTraversal(TreeNode* root) {
        vector res;  // 创建一个空向量,用于存储中序遍历结果
        inorder(root, res);  // 调用inorder函数开始对二叉树进行中序遍历
        return res;  // 返回中序遍历结果向量
    }
};

完整代码程序

#include 
#include 

// 二叉树节点结构体定义
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:
    void inorder(TreeNode* root, vector& res) {
        if (root) {
            inorder(root->left, res);
            res.push_back(root->val);
            inorder(root->right, res);
        }
        else {
            return;
        }
    }

    vector inorderTraversal(TreeNode* root) {
        vector res;
        inorder(root, res);
        return res;
    }
};

int main() {
    // 构建一个简单的二叉树示例,方便测试,实际应用中可根据输入动态构建二叉树
    TreeNode* root = new TreeNode(1);
    TreeNode* node2 = new TreeNode(2);
    TreeNode* node3 = new TreeNode(3);
    TreeNode* node4 = new TreeNode(4);
    TreeNode* node5 = new TreeNode(5);
    TreeNode* node6 = new TreeNode(6);
    TreeNode* node7 = new TreeNode(7);

    root->left = node2;
    root->right = node3;
    node2->left = node4;
    node2->right = node5;
    node3->left = node6;
    node3->right = node7;

    Solution solution;
    std::vector result = solution.inorderTraversal(root);

    for (int val : result) {
        std::cout << val << " ";
    }
    std::cout << std::endl;

    return 0;
}

时间复杂度分析

  1. 分析思路
    对于二叉树的遍历,时间复杂度通常与二叉树的节点个数相关。在中序遍历中,每个节点都会被访问且仅被访问一次,并且递归调用的次数取决于二叉树的节点数量以及树的结构(树的高度等因素也相关,但本质上还是和节点数量紧密联系)。

  2. 具体计算
    假设二叉树的节点个数为 n,由于每个节点都要执行一定的常数级别的操作(如判断、值的添加等),且都被访问一次,所以该算法的时间复杂度为 。无论是最好情况(完全二叉树等结构较规整的树,树高和节点数关系比较稳定)、最坏情况(二叉树退化为链表形式,树高接近节点数 n)还是平均情况,时间复杂度都是 ,因为主要就是遍历所有节点来完成中序遍历操作。

总结

  1. 算法特点及适用场景
    • 特点
      • 利用递归方式实现的二叉树中序遍历算法,代码结构简洁明了,非常符合二叉树这种具有递归结构的数据结构的特点,易于理解和实现。它严格按照中序遍历的规则,能够准确地获取二叉树节点值的中序遍历序列。
      • 时间复杂度为 ,在遍历二叉树节点获取中序遍历结果方面具有较好的效率表现,只要二叉树的节点数量在可接受范围内,都能快速地完成遍历操作。
    • 适用场景
      广泛应用于二叉树相关的各种操作和算法中,比如在对二叉树进行搜索、查找特定节点、判断二叉树是否满足某些性质(如二叉搜索树的合法性判断,常需要结合中序遍历结果来分析)等场景下,首先进行中序遍历获取节点值序列是一个基础且重要的步骤。同时,在对二叉树进行数据提取、展示等应用场景中,中序遍历也可以帮助按照特定顺序整理二叉树中的数据信息。
  2. 注意事项及可能遇到的问题
    • 递归深度限制:由于使用了递归实现,如果二叉树的节点数量非常庞大,可能会导致系统栈的溢出,因为递归调用会不断地在系统栈中压入栈帧记录函数调用信息等,当递归深度超过系统栈所能承受的范围时,程序就会出现错误。在实际应用中,对于可能存在深度较大二叉树的情况,需要考虑是否可以采用非递归的实现方式(如利用栈数据结构模拟递归过程)来避免栈溢出问题。
    • 空指针处理:代码中需要特别注意对空指针的判断和处理,比如在递归调用 inorder(root->left, res) 和 inorder(root->right, res) 时,如果当前节点的左子树或右子树本身就是空的(即 left 或 right 指针为 nullptr),要确保程序能正确地跳过相应的递归调用,直接返回,避免出现对空指针进行非法访问操作导致程序崩溃的情况。

 

你可能感兴趣的:(编程算法提高(c++),算法)