一、中序遍历概述
中序遍历(Inorder Traversal)是二叉树遍历的一种方式,遍历顺序为:左子树 -> 根节点 -> 右子树。这种遍历方式常用于二叉搜索树,可以得到一个升序排列的节点序列。
二、解决方式
第一种方法--递归实现
思路分析
- 递归终止条件:当前节点为null时返回
- 递归过程:
- 先递归遍历左子树
- 访问当前节点(将值加入列表)
- 再递归遍历右子树
- 特点:利用系统调用栈实现回溯,代码简洁易懂
public List<Integer> inorderTraversal1(TreeNode root) {//使用递归方式,返回值是列表,所以需要列表存储,递归的思想是回溯,从叶子节点回溯,而终止条件就是叶子结点为空的时候,返回List<Integer> list = new ArrayList<>();//使用一个辅助方法来递归 inoederHelper(root, list);return list;}private void inoederHelper(TreeNode current, List list){//终止条件if (current == null){return;}//遍历左子树 inoederHelper(current.left, list);//得到回溯的节点,也就是第一个叶子结点,将当前结点添加到列表中 list.add(current.val);//遍历右子树 inoederHelper(current.right,list);}
时间复杂度
- O(n),每个节点都会被访问一次
空间复杂度
- 最坏情况下O(n)(树退化为链表时)
- 平均情况下O(h),h为树的高度
第二种方法--迭代实现
思路分析
- 使用栈模拟递归:显式地使用栈来代替递归中的系统调用栈
- 算法步骤:
- 从根节点开始,将所有左子节点入栈
- 弹出栈顶元素(最左节点)并访问
- 转向该节点的右子树,重复上述过程
- 循环条件:当前节点不为空或栈不为空
- 当前节点不为空:说明还有左子树需要处理
- 栈不为空:说明还有节点需要访问
//使用迭代遍历,思想是利用栈先进后出的原则,从上到下迭代遍历,(从左节点开始)将当前结点入栈,当左子树遍历完后(左节点不为空),出栈,先出的就是左子树的叶子结点了//就可以实现中序遍历的思想,使用while循环,以栈是否为空判断退出循环。使用列表存储节点public List<Integer> inorderTraversal2(TreeNode root) {List<Integer> list = new ArrayList<>();Stack<TreeNode> stack = new Stack<>();TreeNode current = root;while (current != null || !stack.isEmpty()){//遍历左子树while (current != null){stack.push(current);//入栈current = current.left;}//出栈current = stack.pop();list.add(current.val);//遍历右子树current = current.right;}return list;}
时间复杂度
- O(n),每个节点都会被访问一次
空间复杂度
- O(h),h为树的高度,栈的最大深度等于树的高度
测试用例
[1,null,2,3]
输出结果
[1,3,2]
[1,null,2,3]
[1,null,2,3]
# 中序遍历学习笔记
## 一、中序遍历概述
中序遍历(Inorder Traversal)是二叉树遍历的一种方式,遍历顺序为:左子树 -> 根节点 -> 右子树。这种遍历方式常用于二叉搜索树,可以得到一个升序排列的节点序列。
## 二、递归实现
### 代码实现```javapublic List<Integer> inorderTraversal1(TreeNode root) { List<Integer> list = new ArrayList<>(); inoederHelper(root, list); return list;}
private void inoederHelper(TreeNode current, List list) { if (current == null) { return; } inoederHelper(current.left, list); list.add(current.val); inoederHelper(current.right, list);}```
### 思路分析1. **递归终止条件**:当前节点为null时返回2. **递归过程**: - 先递归遍历左子树 - 访问当前节点(将值加入列表) - 再递归遍历右子树3. **特点**:利用系统调用栈实现回溯,代码简洁易懂
### 时间复杂度- O(n),每个节点都会被访问一次
### 空间复杂度- 最坏情况下O(n)(树退化为链表时)- 平均情况下O(h),h为树的高度
## 三、迭代实现
### 代码实现```javapublic List<Integer> inorderTraversal2(TreeNode root) { List<Integer> list = new ArrayList<>(); Stack<TreeNode> stack = new Stack<>(); TreeNode current = root; while (current != null || !stack.isEmpty()) { while (current != null) { stack.push(current); current = current.left; } current = stack.pop(); list.add(current.val); current = current.right; } return list;}```
### 思路分析1. **使用栈模拟递归**:显式地使用栈来代替递归中的系统调用栈2. **算法步骤**: - 从根节点开始,将所有左子节点入栈 - 弹出栈顶元素(最左节点)并访问 - 转向该节点的右子树,重复上述过程3. **循环条件**:当前节点不为空或栈不为空 - 当前节点不为空:说明还有左子树需要处理 - 栈不为空:说明还有节点需要访问
### 时间复杂度- O(n),每个节点都会被访问一次
### 空间复杂度- O(h),h为树的高度,栈的最大深度等于树的高度
## 四、两种实现方式对比
| 特性 | 递归实现 | 迭代实现 ||-------------|-----------------------|-----------------------|| 代码复杂度 | 简单 | 较复杂 || 空间复杂度 | 隐式系统栈,可能溢出 | 显式栈,更可控 || 适用场景 | 树深度不大时 | 树深度较大时 || 可读性 | 高 | 中等 |
## 五、测试用例验证
测试用例构建的二叉树结构:``` 1 / \ 2 3 / \ \ 4 5 6 / 7```
预期中序遍历结果:`[4, 2, 5, 1, 7, 6, 3]`
两种实现方式均输出正确结果,验证了代码的正确性。
## 六、总结
1. 递归实现简洁直观,适合理解中序遍历的基本原理2. 迭代实现效率更高,适合处理深度较大的树3. 两种实现方式的核心思想相同:左-根-右的访问顺序4. 在实际应用中,可以根据具体情况选择合适的方法: - 面试中通常要求掌握两种实现 - 实际项目中,树不大时可用递归,树很深时建议用迭代
## 七、可能的改进方向
1. 可以添加Morris遍历的实现,实现O(1)空间复杂度的中序遍历2. 对于迭代实现,可以增加对栈溢出异常的处理3. 可以扩展为通用的二叉树遍历模板,只需调整访问顺序即可实现前序、中序、后序遍历