算法leetcode|94. 二叉树的中序遍历(多语言实现)


文章目录

  • 94. 二叉树的中序遍历:
    • 样例 1:
    • 样例 2:
    • 样例 3:
    • 提示:
  • 分析:
  • 题解:
    • rust:
    • go:
    • c++:
    • python:
    • java:


94. 二叉树的中序遍历:

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

样例 1:

输入:root = [1,null,2,3]输出:[1,3,2]

样例 2:

输入:root = []输出:[]

样例 3:

输入:root = [1]输出:[1]

提示:

  • 树中节点数目在范围 [0, 100]
  • -100 <= Node.val <= 100

分析:

  • 面对这道算法题目,二当家的再次陷入了沉思。
  • 二叉树的中序遍历和前序遍历,后续遍历是二叉树常用的遍历方式。
  • 使用递归方式比循环非递归方式更加简单,直观,易于理解。
  • 通常二叉树的中序遍历一定要使用一个栈结构,因为中序遍历的要求是遍历完左子树才能遍历当前节点,但是遍历到了左子树就无法再回到当前节点了,所以一般都是使用压栈的方式,先将当前节点压栈,遍历完左子树再将当前节点出栈,这样空间复杂度就会是 O(n) (递归也相当于使用了栈结构)。
  • 说起来这不是什么大问题,但是算法就是要想办法优化降低时间和空间的复杂度,于是寄出一种可以将空间复杂度降低为 O(1) 的中序遍历方式,Morris 中序遍历。
  • 事实上Morris 中序遍历不是没有代价的,由于要做额外的节点连接和恢复,相当于用时间换空间。

题解:

rust:

// Definition for a binary tree node.
// #[derive(Debug, PartialEq, Eq)]
// pub struct TreeNode {
//   pub val: i32,
//   pub left: Option<Rc<RefCell<TreeNode>>>,
//   pub right: Option<Rc<RefCell<TreeNode>>>,
// }
//
// impl TreeNode {
//   #[inline]
//   pub fn new(val: i32) -> Self {
//     TreeNode {
//       val,
//       left: None,
//       right: None
//     }
//   }
// }
use std::rc::Rc;
use std::cell::RefCell;
impl Solution {pub fn inorder_traversal(mut root: Option<Rc<RefCell<TreeNode>>>) -> Vec<i32> {let mut ans = Vec::new();while root != None {if root.as_ref().unwrap().borrow().left != None {// 寻找当前 root 节点的前驱节点:前驱 predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止let mut predecessor = root.as_ref().unwrap().borrow().left.clone();while predecessor.as_ref().unwrap().borrow().right != None&& predecessor.as_ref().unwrap().borrow().right != root {predecessor = predecessor.unwrap().borrow().right.clone();}if predecessor.as_ref().unwrap().borrow().right == None {// 让前驱 predecessor 节点的右指针指向当前 root 节点,继续遍历左子树,之后会再次回到当前 root 节点predecessor.unwrap().borrow_mut().right = root.clone();// 遍历左子树root = root.unwrap().borrow().left.clone();continue;} else {// 左子树遍历完毕又回到了当前 root 节点,让前驱 predecessor 节点的右指针与当前 root 节点断开,恢复原样predecessor.unwrap().borrow_mut().right = None;}}// 遍历当前 root 节点ans.push(root.as_ref().unwrap().borrow().val);// 遍历当前 root 节点的右子树root = root.unwrap().borrow().right.clone();}return ans;}
}

go:

/*** Definition for a binary tree node.* type TreeNode struct {*     Val int*     Left *TreeNode*     Right *TreeNode* }*/
func inorderTraversal(root *TreeNode) []int {var ans []intfor root != nil {if root.Left != nil {// 寻找当前 root 节点的前驱节点:前驱 predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止predecessor := root.Leftfor predecessor.Right != nil && predecessor.Right != root {// 有右子树且没有设置过指向 root,则继续向右走predecessor = predecessor.Right}if predecessor.Right == nil {// 让前驱 predecessor 节点的右指针指向当前 root 节点,继续遍历左子树,之后会再次回到当前 root 节点predecessor.Right = root// 遍历左子树root = root.Leftcontinue} else {// 左子树遍历完毕又回到了当前 root 节点,让前驱 predecessor 节点的右指针与当前 root 节点断开,恢复原样predecessor.Right = nil}}// 遍历当前 root 节点ans = append(ans, root.Val)// 遍历当前 root 节点的右子树root = root.Right}return ans
}

c++:

/*** 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> inorderTraversal(TreeNode* root) {vector<int> ans;while (root != nullptr) {if (root->left != nullptr) {// 寻找当前 root 节点的前驱节点:前驱 predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止TreeNode *predecessor = root->left;while (predecessor->right != nullptr && predecessor->right != root) {predecessor = predecessor->right;}if (predecessor->right == nullptr) {// 让前驱 predecessor 节点的右指针指向当前 root 节点,继续遍历左子树,之后会再次回到当前 root 节点predecessor->right = root;// 遍历左子树root = root->left;continue;} else {// 左子树遍历完毕又回到了当前 root 节点,让前驱 predecessor 节点的右指针与当前 root 节点断开,恢复原样predecessor->right = nullptr;}}// 遍历当前 root 节点ans.emplace_back(root->val);// 遍历当前 root 节点的右子树root = root->right;}return ans;}
};

python:

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:ans = list()while root is not None:if root.left is not None:# 寻找当前 root 节点的前驱节点:前驱 predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止predecessor = root.leftwhile predecessor.right is not None and predecessor.right != root:# 有右子树且没有设置过指向 root,则继续向右走predecessor = predecessor.rightif predecessor.right is None:# 让前驱 predecessor 节点的右指针指向当前 root 节点,继续遍历左子树,之后会再次回到当前 root 节点predecessor.right = root# 遍历左子树root = root.leftcontinueelse:# 左子树遍历完毕又回到了当前 root 节点,让前驱 predecessor 节点的右指针与当前 root 节点断开,恢复原样predecessor.right = None# 遍历当前 root 节点ans.append(root.val)# 遍历当前 root 节点的右子树root = root.rightreturn ans

java:

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> ans = new ArrayList<Integer>();while (root != null) {if (root.left != null) {// 寻找当前 root 节点的前驱节点:前驱 predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止TreeNode predecessor = root.left;while (predecessor.right != null && predecessor.right != root) {predecessor = predecessor.right;}if (predecessor.right == null) {// 让前驱 predecessor 节点的右指针指向当前 root 节点,继续遍历左子树,之后会再次回到当前 root 节点predecessor.right = root;// 遍历左子树root = root.left;continue;} else {// 左子树遍历完毕又回到了当前 root 节点,让前驱 predecessor 节点的右指针与当前 root 节点断开,恢复原样predecessor.right = null;}}// 遍历当前 root 节点ans.add(root.val);// 遍历当前 root 节点的右子树root = root.right;}return ans;}
}

非常感谢你阅读本文~
欢迎【点赞】【收藏】【评论】三连走一波~
放弃不难,但坚持一定很酷~
希望我们大家都能每天进步一点点~
本文由 二当家的白帽子:https://le-yi.blog.csdn.net/ 博客原创~


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring高手之路-Spring AOP

目录 什么是AOP Spring AOP有如下概念 补充&#xff1a; AOP是如何实现的 Spring AOP 是通过代理模式实现的。 Spring AOP默认使用标准的JDK动态代理进行AOP代理。 什么是AOP AOP(Aspect-Oriented Programming)&#xff0c;即面向切面编程&#xff0c;用人话说就是把公共的…

开发人工智能 需要什么工具

人工智能&#xff08;Artificial Intelligence, AI&#xff09;是指利用计算机模拟、扩展和延伸人类智能的理论、方法、技术和应用系统的一门学科。人工智能研究的目标是使计算机能够像人类一样具有智能&#xff0c;能够感知和理解环境、学习和推理、决策和规划&#xff0c;具备…

jar 运行清单文件MANIFEST.MF生成定义Main-Class Premain-Class IDEA maven-assembly-plugin

可运行jar文件中的启动清单文件 META-INF/MANIFEST.MF 内容自定义生成 清单文件中的 Main-Class: Premain-Class: Can-Retransform-Classes: 在maven-assembly-plugin插件中的生成配置如下, 注意命名 <archive> <manifest> <mainClass>c…

MySQL中的like模糊查询

like与通配符搭配使用进行模糊查询 通配符 % 表示任意多个字符 _ 表示任意单个字符 #用户名中包含a的 select * from user where name like %a% #用户名中第二个字符为a的 select * from user where name like _a% #用户名中第二个字符为_的(需要使用转义字符) select * from u…

强烈推荐,提高开发效率的IDEA插件

自动生成JVM诊断工具arthas的命令 arthas idea (2.36) 基于IntelliJ IDEA开发的Alibaba Arthas 命令生成插件&#xff0c;支持Alibaba Arthas 官方常用的命令。 官方的命令太复杂&#xff0c;此插件为了简化使用者的难度&#xff0c;提供了一些常用命令的支持 该插件支持Aliba…

Dockerfile构建镜像

Dockerfile构建镜像 Dockerfile 是一个文本格式的配置文件&#xff0c; 用户可以使用 Dockerfile 来快速创建自定义的镜像&#xff0c;另外&#xff0c;使 用Dockerfile去构建镜像好比使用pom去构建maven项目一样&#xff0c;有异曲同工之妙 基本结构 Dockerfile 由一行行…

Intel® SGX Instruction References(五)

文章目录 前言一、Intel SGX Instruction Syntax and Operation1.1 ENCLS Register Usage Summary1.2 ENCLU Register Usage Summary1.3 ENCLV Register Usage Summary1.4 Information and Error Codes1.5 Internal CREGs1.6 Concurrent Operation Restrictions 二、Intel SGX …

Nodejs 使用 fs-extra 模块进行目录和文件操作

在Node.js中&#xff0c;fs-extra模块是fs模块的扩展&#xff0c;提供了一些额外的功能&#xff0c;使文件和目录的操作更加便捷。那么我们如何使用 fs-extra 模块进行目录和文件操作呢&#xff1f;以下是一些常见的用法示例&#xff1a; 1. ensureDir/ensureDirSync - 创建目…

WuBit:聚合BRC20资产交易,续写铭文市场新浪潮

2023年的数字货币领域见证了重大的创新&#xff0c;尤其是比特币Ordinals协议的推出&#xff0c;它为铭文赛道带来了新的生命力。这一协议的核心在于将比特币分割为更小的单位——聪&#xff0c;并在每个聪上嵌入独特信息&#xff0c;从而创建了一种新颖的非同质化资产。BRC20等…

亚马逊云科技 re:Invent 2023 产品体验:亚马逊云科技产品应用实践 王炸产品 Amazon Q,你的 AI 助手

意料之中 2023年9月25日&#xff0c;亚马逊宣布与 Anthropic 正式展开战略合作&#xff0c;结合双方在更安全的生成式 AI 领域的先进技术和专业知识&#xff0c;加速 Anthropic 未来基础模型的开发&#xff0c;并将其广泛提供给亚马逊云科技的客户使用。 亚马逊云科技开发者社…

TypeScript学习(基础篇)

前言 在现代的Web开发生态系统中&#xff0c;JavaScript已经成为一种必备的技术。然而&#xff0c;随着应用的增大&#xff0c;JavaScript的一些限制开始显现&#xff0c;例如缺乏静态类型检查和编译时错误检查。这正是TypeScript发挥作用的地方&#xff0c;TypeScript是一种静…

GO语言基础笔记(四):并发编程基础

目录 Goroutines 通道&#xff08;Channel&#xff09; 代码示例 Goroutines 定义与特点&#xff1a; Goroutines是Go语言中实现并发的基本单位。它比传统的线程更轻量级&#xff0c;拥有更小的内存占用和更快的启动时间。在Go程序中&#xff0c;您可以轻松地启动成千上万的G…

Redis基本介绍和使用场景

1、什么是Redis Redis 是一种基于内存的数据库&#xff0c;对数据的读写操作都是在内存中完成&#xff0c;因此读写速度非常快&#xff0c;常用于缓存&#xff0c;消息队列、分布式锁等场景。 Redis 提供了多种数据类型来支持不同的业务场景&#xff0c;比如 String(字符串)、…

【Linux系统基础】(2)在Linux上部署MySQL、RabbitMQ、ElasticSearch等各类软件

实战章节&#xff1a;在Linux上部署各类软件 前言 为什么学习各类软件在Linux上的部署 在前面&#xff0c;我们学习了许多的Linux命令和高级技巧&#xff0c;这些知识点比较零散&#xff0c;同学们跟随着课程的内容进行练习虽然可以基础掌握这些命令和技巧的使用&#xff0c;…

vue跑马灯

vue跑马灯 组件propscolOption 使用elementui的布局组件实现列宽度 <template><div class"lamp-container"><div class"lamp-header"><el-row :gutter"10"><el-col v-for"col in column" :key"col.pr…

12.26ARM作业

三个按键中断&#xff0c;控制对应灯亮灭 main.c #include "key_it.h"void delay(int ms){int i,j;for(i0;i<ms;i){for(j0;j<2000;j);}}int main(){all_led_init();key1_it_config();key2_it_config();key3_it_config();while(1){printf("do main...\n&…

Linux c++开发-14-IO复用

什么是文件 程序员使用I/O最终都逃不过文件这个概念。 在Linux世界中文件是一个很简单的概念&#xff0c;作为程序员我们只需要将其理解为一个N byte的序列就可以了&#xff1a; b1, b2, b3, b4, … bN 实际上所有的I/O设备都被抽象为了文件这个概念&#xff0c;一切皆文件…

常用命令-设置

目录 系统配置查看系统架构屏幕演示工具合并终端命令Windows cmd命令提示符重启网卡禁止系统更新CMD运行powshell获取文件安装目录微软VC运行库合集强制刷新IP默认程序打开文件SSH免密登录关闭IE增强配置警告滑动关机最近操作记录解决谷歌翻译禁止系统休眠文件传输文件批量改名…

基于Java (spring-boot)的宠物管理系统

一、项目介绍 1、用户端功能&#xff1a; 首页&#xff1a;展示公告列表&#xff0c;宠物科普&#xff0c;介绍流浪宠物&#xff0c;热门活动。 宠物领养&#xff1a;用户搜索想要领养宠物&#xff0c;申请领养&#xff0c;查看自己领养的宠物。 宠物救助&#xff1a;用户能…

C# .Net学习笔记—— 加密和解密算法

一、四种加密方式 1、MD5不可逆加密 2、Des对称可逆加密 3、RSA非对称可逆加密 4、数字证书SSL 二、详解 1、MD5加密 public class MD5Encrypt{public static string Encrypt(string source, int length 32){if (string.IsNullOrEmpty(source)) return string.Empty;HashA…