【力扣 - 二叉树的中序遍历】

题目描述

给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
在这里插入图片描述

提示:

树中节点数目在范围 [0, 100]

-100 <= Node.val <= 100

方法一:递归

思路与算法

首先我们需要了解什么是二叉树的中序遍历:按照访问左子树——根节点——右子树的方式遍历这棵树,而在访问左子树或者右子树的时候我们按照同样的方式遍历,直到遍历完整棵树。因此整个遍历过程天然具有递归的性质,我们可以直接用递归函数来模拟这一过程。

定义 inorder(root) 表示当前遍历到 root 节点的答案,那么按照定义,我们只要递归调用 inorder(root.left) 来遍历 root 节点的左子树,然后将 root 节点的值加入答案,再递归调用inorder(root.right) 来遍历 root 节点的右子树即可,递归终止的条件为碰到空节点。

代码

/**
 * Definition for a binary tree node.
 */
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
 /*
  * The  inorder  function performs an inorder traversal of a binary tree recursively. 
  * It stores the values of the nodes in the result array  res  and increments the size  resSize  accordingly. 
 */
 /*
  * The function "inorder" in the provided code is a recursive function. 
  * When a function calls itself inside its own definition, it is known as recursion. 
  * In this case, the "inorder" function is designed to perform an inorder traversal of a binary tree. 
  * 1. The "inorder" function is called with the root node of the binary tree.
  * 2. Inside the function, it first checks if the current node is NULL. If it is NULL, the function returns and the recursion stops.
  * 3. If the current node is not NULL, the function recursively calls itself for the left child of the current node (root->left). This step continues until it reaches a NULL node (i.e., the left subtree is fully traversed).
  * 4. After traversing the left subtree, the function stores the value of the current node in the result array and increments the size of the result array.
  * 5. Finally, the function recursively calls itself for the right child of the current node (root->right) to traverse the right subtree.
  * This recursive process repeats for each node in the binary tree, 
  * effectively performing an inorder traversal by visiting the nodes in the order of left subtree - current node - right subtree. 
  * Each recursive call maintains its own set of variables and execution context, 
  * allowing the function to traverse the entire tree in an ordered manner.
  */ 
void inorder(struct TreeNode* root, int* res, int* resSize) {
    // Check if the current node is NULL
    if (!root) {
        return;  // Return if the current node is NULL
    }
    
    // Traverse the left subtree in inorder
    inorder(root->left, res, resSize);
    
    // Store the value of the current node in the result array and increment the size
    res[(*resSize)++] = root->val;
    
    // Traverse the right subtree in inorder
    inorder(root->right, res, resSize);
    /*
     * `res[(*resSize)++] = root->val;`  is not needed here,
     * because the inorder traversal of a binary tree is structured in such a way that after traversing the left subtree and the current node, 
     * the traversal of the right subtree will naturally continue the process of storing the values in the correct order in the result array  `res` .
     * In an inorder traversal, the sequence of operations ensures that the left subtree is fully explored before visiting the current node, 
     * and then the right subtree is explored after the current node. 
     * Therefore, by the time the function returns from the recursive call  `inorder(root->right, res, resSize);` , 
     * the right subtree has been traversed and the values have been stored in the result array in the correct order relative to the current node.
     * Including  `res[(*resSize)++] = root->val;`  after the right subtree traversal would result in duplicating the value of the current node in the result array, 
     * which is unnecessary and would disrupt the correct inorder traversal sequence.
     */
}
/*
 * The  inorderTraversal  function initializes the result array, 
 * calls the  inorder  function to perform the traversal, and then returns the result array. 
 */ 
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
    // Allocate memory for the result array
    // Create an integer array of size 501 dynamically on the heap and assigning the address of the first element of the array to the pointer variable  res .
    int* res = malloc(sizeof(int) * 501);
    
    // Initialize the return size to 0
    *returnSize = 0;
    
    // Perform inorder traversal starting from the root node
    inorder(root, res, returnSize);
    
    // Return the result array containing the inorder traversal of the binary tree
    return res;
}

复杂度分析

时间复杂度:O(n),其中 n 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。
空间复杂度:O(n)。空间复杂度取决于递归的栈深度,而栈深度在二叉树为一条链的情况下会达到 O(n)的级别。

方法二:迭代

思路与算法

方法一的递归函数我们也可以用迭代的方式实现,两种方式是等价的,区别在于递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显式地将这个栈模拟出来,其他都相同。
在这里插入图片描述

代码

/**
 * Definition for a binary tree node.
 */
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};
/**
 * An iterative version of the inorder traversal of a binary tree without using recursion.
 */
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
    // Initialize return size to 0
    *returnSize = 0;
    
    // Allocate memory for the result array
    int* res = malloc(sizeof(int) * 501);
    
    // Allocate memory for the stack to keep track of nodes
    struct TreeNode** stk = malloc(sizeof(struct TreeNode*) * 501);
    
    // Initialize top of the stack
    // variable top to keep track of the top of the stack. 
    int top = 0;
    
    // Iterative inorder traversal using a stack
    // The while loop continues until the current node  root  is NULL and the stack is empty (indicated by  top > 0 ).
    while (root != NULL || top > 0) {
        // Traverse left subtree and push nodes onto the stack 
        // a nested while loop to traverse the left subtree of the current node and pushes each node onto the stack. 
        while (root != NULL) {
            stk[top++] = root;
            root = root->left;
        }
        // Check if the stack is not empty before popping
        if (top > 0)
        {
        	// Once the left subtree is fully traversed
        	// Pop a node from the stack
        	root = stk[--top];
        
        	// Add the value of the popped node to the result array
        	res[(*returnSize)++] = root->val;
        
        	// Move to the right child of the popped node
        	root = root->right;
        }
    }
    // Free the memory allocated for the stack
    free(stk);
    
    // Return the result array containing inorder traversal
    return res;
}

复杂度分析

时间复杂度:O(n),其中 n 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。

空间复杂度:O(n)。空间复杂度取决于栈深度,而栈深度在二叉树为一条链的情况下会达到 O(n)的级别。

方法三:Morris 中序遍历

思路与算法

Morris 遍历算法是另一种遍历二叉树的方法,它能将非递归的中序遍历空间复杂度降为 O(1)。

Morris 遍历算法整体步骤如下(假设当前遍历到的节点为 xxx):

  1. 如果 xxx 无左孩子,先将 xxx 的值加入答案数组,再访问 xxx 的右孩子,即 x=x.right
  2. 如果 xxx 有左孩子,则找到 xxx 左子树上最右的节点(即左子树中序遍历的最后一个节点,xxx 在中序遍历中的前驱节点),我们记为 predecessor。根据 predecessor 的右孩子是否为空,进行如下操作。
    • 如果 predecessor 的右孩子为空,则将其右孩子指向 xxx,然后访问 xxx 的左孩子,即 x=x.left
    • 如果 predecessor\ 的右孩子不为空,则此时其右孩子指向 xxx,说明我们已经遍历完 xxx 的左子树,我们将 predecessor 的右孩子置空,将 xxx 的值加入答案数组,然后访问 xxx 的右孩子,即 x=x.right
  3. 重复上述操作,直至访问完整棵树。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    其实整个过程我们就多做一步:假设当前遍历到的节点为 x,将 x 的左子树中最右边的节点的右孩子指向 x,这样在左子树遍历完成后我们通过这个指向走回了 x,且能通过这个指向知晓我们已经遍历完成了左子树,而不用再通过栈来维护,省去了栈的空间复杂度。

代码

/**
 * Definition for a binary tree node.
 */
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};
/**
 * The algorithm uses a predecessor node to establish temporary links 
 * between nodes to simulate the recursive call stack 
 * that would be used in a recursive inorder traversal. 
 * This approach allows for an iterative inorder traversal of the binary tree.
 */
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
    // Allocate memory for the result array
    int* res = malloc(sizeof(int) * 501);
    
    // Initialize return size to 0
    *returnSize = 0;
    
    // Initialize predecessor node to NULL
    struct TreeNode* predecessor = NULL;
    
    // Traverse the tree in inorder without using recursion
    while (root != NULL) {
        // If the current node has a left child
        if (root->left != NULL) {
            // Find the predecessor node, which is the rightmost node in the left subtree
            predecessor = root->left;
            while (predecessor->right != NULL && predecessor->right != root) {
                predecessor = predecessor->right;
            }
            
            // If predecessor's right child is NULL, establish a link and move to the left child
            if (predecessor->right == NULL) {
                predecessor->right = root;
                root = root->left;
            }
            // If the left subtree has been visited, disconnect the link and move to the right child
            else {
                res[(*returnSize)++] = root->val;
                predecessor->right = NULL;
                root = root->right;
            }
        }
        // If there is no left child, visit the current node and move to the right child
        else {
            res[(*returnSize)++] = root->val;
            root = root->right;
        }
    }
    
    // Return the result array containing inorder traversal
    return res;
}

复杂度分析

时间复杂度:O(n),其中 n 为二叉树的节点个数。Morris 遍历中每个节点会被访问两次,因此总时间复杂度为 O(2n)=O(n)。

空间复杂度:O(1)。

作者:力扣官方题解
链接:https://leetcode.cn/problems/binary-tree-inorder-traversal/solutions/412886/er-cha-shu-de-zhong-xu-bian-li-by-leetcode-solutio/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/395042.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux系统化学习】深入理解文件系统(Ext2文件系统)

目录 前言 磁盘的物理结构 物理结构 磁头和盘片工作解析图 盘面区域划分图&#xff08;俯视盘面图&#xff09; 扇区的寻址、定位&#xff08;CHS定位&#xff09; 磁盘存储的逻辑抽象结构 LBA定址 文件系统 磁盘分区 EXT2文件系统 组块中的信息介绍 查看inode编号…

代码随想录算法训练营|二叉树总结

二叉树的定义&#xff1a; struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode():val(0),left(nullptr),right(nullptr){}TreeNode(int val):val(val),left(nullptr),right(nullptr){}TreeNode(int val,TreeNode* left,TreeNode* right):val(val),left(left),…

【Linux】软件包管理器 yum | vim编辑器

前言: 软件包管理器 yum和vim编辑器讲解 文章目录 软件包管理器 yum编辑器-vim四种模式普通模式批量化注释和批量化去注释末行模式临时文件 软件包管理器 yum yum&#xff08;Yellowdog Updater, Modified&#xff09;是一个在基于 RPM&#xff08;管理软件包的格式和工具集合&…

Smart Link和Monitor Link简介

定义 Smart Link&#xff0c;又叫做备份链路。一个Smart Link由两个接口组成&#xff0c;其中一个接口作为另一个的备份。Smart Link常用于双上行组网&#xff0c;提供可靠高效的备份和快速的切换机制。 Monitor Link是一种接口联动方案&#xff0c;它通过监控设备的上行接口…

Facebook Horizon:探索虚拟现实中的社交空间

随着科技的不断进步&#xff0c;虚拟现实&#xff08;VR&#xff09;技术正成为社交互动和娱乐体验的新前沿。在这个数字时代&#xff0c;Facebook作为全球最大的社交媒体平台之一&#xff0c;正在引领虚拟社交的新时代&#xff0c;其推出的虚拟社交平台Facebook Horizon成为了…

手持三防平板丨国产化加固平板丨国产三防平板发展的意义是什么?

随着现代科技的快速发展&#xff0c;平板电脑在我们的生活中扮演着越来越重要的角色。然而&#xff0c;传统的平板电脑只能在普通的环境中使用&#xff0c;而无法在恶劣的环境中使用&#xff0c;例如在高海拔、高温、高湿度、沙漠等环境中&#xff0c;传统平板电脑往往会出现故…

自定义Linux登录自动提示语

设置提示语的方式 在Linux系统中&#xff0c;可以通过修改几个特定的文件来实现在用户登录时自动弹出提示语。以下是几个常用的方法&#xff1a; 1. 修改/etc/issue文件&#xff1a; 这个文件用于显示本地登录前的提示信息 sudo vi /etc/issue在项目合作的时候&#xff0c;…

Zoho Desk ‘24|了解客戶支持系統所有新的內容

Zoho Desk 是一款在線客戶工單管理系統&#xff0c;它的核心是以“客戶”爲宗旨&#xff0c;幫助企業從多種渠道爲客戶提供優質的售後服務支持&#xff0c;持續提升客戶滿意度和忠誠度。我們很榮幸地推出Zoho Desk 24,本篇文章我們將會介紹它的新功能以及更新地部分&#xff0c…

Shiro-11-web 介绍

配置 将Shiro集成到任何web应用程序的最简单方法是在web.xml中配置一个Servlet ContextListener和过滤器&#xff0c;该Servlet了解如何读取Shiro的INI配置。 INI配置格式本身的大部分是在配置页面的INI部分中定义的&#xff0c;但是我们将在这里介绍一些额外的特定于web的部…

linux基础学习(10):基本权限与相关命令

1.基本权限 用ls -l查看当前目录文件时&#xff0c;可以看到文件的基本权限 其由10位组成&#xff0c;其中&#xff1a; 第1位&#xff1a;代表文件类型。 - d lbc普通文件目录文件软链接文件块设备文件&#xff0c;也就是硬盘等存储设备的文件字符设备文件&#xff0c;是鼠…

嵌入式day24

开课复工啦~ 冲冲冲&#xff01; 文件IO&#xff1a; read函数和write函数&#xff1a; &#x1f4da; write 接口有三个参数&#xff1a; fd&#xff1a;文件描述符buf&#xff1a;要写入的缓冲区的起始地址&#xff08;如果是字符串&#xff0c;那么就是字符串的起始地址&…

前端新手Vue3+Vite+Ts+Pinia+Sass项目指北系列文章 —— 第十二章 常用工具函数 (Utils配置)

前言 在项目开发中&#xff0c;我们经常会使用一些工具函数&#xff0c;也经常会用到例如loadsh等工具库&#xff0c;但是这些工具库的体积往往比较大&#xff0c;如果项目本身已经引入了这些工具库&#xff0c;那么我们就没有必要再引入一次&#xff0c;所以我们需要自己封装…

基于JAVA+SpringBoot+Vue的前后端分离的电影院售票管理运营平台

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 该系统研究背景聚焦于…

【日常聊聊】深度学习进度

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;日常聊聊 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 方向一&#xff1a;深度学习的基本原理和算法 方向二&#xff1a;深度学习的应用实例 方向三&#xff1a;深度学习的挑战和未…

vue3项目引入本地js文件,实现一个音频播放按钮

目前有一个需求就是在网页上放置一个音乐控制按钮&#xff0c;并且是在vue3项目里面。于是小白的我遇到了2个问题&#xff0c;第一个问题是如何实现没有进度条的播放按钮&#xff0c;这个网上有现成的代码&#xff0c;可以通过js代码切换不同的图片或者是别的样式&#xff0c;并…

【c语言】c语言转义字符详解

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;c语言 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步&…

【千帆平台】使用千帆大模型平台创建自定义模型调用API,贺岁灵感模型,文本对话

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《千帆平台》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和…

html的表单标签(上):form标签和input标签

表单标签 表单是让用户输入信息的重要途径。 用表单标签来完成与服务器的一次交互&#xff0c;比如你登录QQ账号时的场景。 表单分成两个部分&#xff1a; 表单域&#xff1a;包含表单元素的区域&#xff0c;用form标签来表示。表单控件&#xff1a;输入框&#xff0c;提交按…

黑马程序员-瑞吉外卖day9

菜品分类下拉列表 CategoryController里面写 /*** 根据条件查询分类数据** param category* return*/GetMapping("/list")ApiOperation("菜品分类目录")public R<List<Category>> list(Category category) {List<Category> list cate…

改进Rust与C++的互操作性,谷歌向 Rust 基金会捐赠100万美元

hello宝子们...我们是艾斯视觉擅长ui设计和前端开发10年经验&#xff01;希望我的分享能帮助到您&#xff01;如需帮助可以评论关注私信我们一起探讨&#xff01;致敬感谢感恩&#xff01; 标题&#xff1a;谷歌向 Rust 基金会捐赠 100 万美元&#xff0c;致力于提升 Rust 与 C…