题意
给定一个二叉树的根节点 root ,返回它的 中序 遍历。
难度
简单
示例
示例 1:
输入:root=[1,null,2,3]
输出:[1,3,2]
示例 2:
输入:root=[]
输出:[]
示例 3:
输入:root=[1]
输出:[1]
分析
对于这道题,我们需要先搞清楚,什么是二叉树的中序遍历。
前序遍历、中序遍历、后序遍历是二叉树的三种基本遍历方式,它们的主要区别在于访问根节点的时间。
- 前序遍历 (Preorder Traversal)
- 先访问根节点
- 接着访问左子树
- 最后访问右子树
- 遍历顺序:根 -> 左 -> 右
- 中序遍历 (Inorder Traversal)
- 先访问左子树
- 接着访问根节点
- 最后访问右子树
- 遍历顺序:左 -> 根 -> 右
- 后序遍历 (Postorder Traversal)
- 先访问左子树
- 接着访问右子树
- 最后访问根节点
- 遍历顺序:左 -> 右 -> 根
这里是一个简单的二叉树示意图:
A
/\
BC
/\/\
DEFG
对于这个二叉树:
- 前序遍历的结果是:A, B, D, E, C, F, G
- 中序遍历的结果是:D, B, E, A, F, C, G
- 后序遍历的结果是:D, E, B, F, G, C, A
我们可以写出一个中序遍历大概的算法雏形
public void traversal(TreeNode node){
if(node.left!=null){
traversal(node.left);
}
read(node);
if(node.right!=null){
traversal(node.right);
}
}
同理我们稍微变换一下也可以得到对应的前序和后序遍历的算法
public void traversal(TreeNode node){
read(node);
if(node.left!=null){
traversal(node.left);
}
if(node.right!=null){
traversal(node.right);
}
}
public void traversal(TreeNode node){
if(node.left!=null){
traversal(node.left);
}
if(node.right!=null){
traversal(node.right);
}
read(node);
}
最后我们只需要稍微对边界条件进行限制即可得到最终的算法。
/**
* 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 {
//创建一个列表,用于存放中序遍历的结果
List<Integer> res = new ArrayList<>();
//主方法,开始中序遍历
public List<Integer> inorderTraversal(TreeNode root){
//如果树不为空,则开始遍历
if(root!=null){
traversal(root);
}
//返回中序遍历的结果
return res;
}
//读取当前节点的值,并将其添加到结果列表中
public void read(TreeNode node){
res.add(node.val);
}
//中序遍历的核心逻辑
public void traversal(TreeNode node){
//如果当前节点的左子树不为空,先遍历左子树
if(node.left!=null){
traversal(node.left);
}
//访问当前节点
read(node);
//如果当前节点的右子树不为空,接着遍历右子树
if(node.right!=null){
traversal(node.right);
}
}
}
当输入为 1,null,2,3,二叉树可以表示为以下结构:
1
\
2
/
3
我们来模拟一下题解中的中序遍历过程。
- 初始调用:从根节点1开始调用inorderTraversal。
- 在inorderTraversal中,检查根节点1是否为null。既然不是,继续调用traversal。
- 在traversal中:
- 先检查节点1的左子树是否存在。但节点1的左子树是null,所以不做进一步递归。
- 然后,调用read方法访问节点1并将值1添加到结果列表res中。
- 接着,它会遍历节点1的右子树,即节点2。
- 为节点2重复步骤3:
- 先检查节点2的左子树。这里,节点2有一个左子节点3,所以它递归调用traversal为节点3。
- 对于节点3,它没有左子树,所以它直接访问该节点,并将值3添加到结果列表res中。
- 节点3没有右子树,所以没有进一步的递归调用。
- 回到节点2,在访问了它的左子树后,现在访问节点2本身,并将值2添加到结果列表res中。
- 节点2没有右子树,所以此处的遍历结束。
- 在完成以上所有步骤后,遍历完成。
根据以上描述,结果列表res为:[1, 3, 2]。
来看一下题解效率:
总结
递归处理是处理树形结构中最常用的一个操作,因为树的结构本质与递归殊途同归,在逻辑结构上二者十分近似,所以在学习树结构时,我们通常都可以类比递归,并从中获得启发。
力扣链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
一步一个脚印
不积跬步无以至千里,不积小流无以成江海。LeetCode - 100天从算法小白到卷王正式启动了