二叉树题解——二叉树的中序遍历【LeetCode】统一写法版本

发布于:2025-07-03 ⋅ 阅读:(30) ⋅ 点赞:(0)

94. 二叉树的中序遍历

一、算法逻辑(逐步通顺地讲解)

这段代码的目标是实现中序遍历,即按照顺序:
左子树 → 当前节点 → 右子树 遍历整个二叉树,并返回节点值的列表。

与常见的递归或传统栈方法不同,这里使用的是一种“统一写法”技巧,将“节点值访问”与“节点展开”分开处理,流程如下:


1️⃣ 初始化结构

  • 使用一个栈保存待处理元素(可能是 TreeNodeint);

  • 初始栈中放入整棵树的根节点;

  • 结果数组 rst 用来保存最终遍历的顺序。


2️⃣ 遍历栈(模拟递归展开)

不断从栈中弹出元素 i,分两种情况处理:

▶️ 情况一:i 是一个树节点(TreeNode
  • 说明它还没有“展开”处理;

  • 根据中序遍历的顺序(左 → 根 → 右),将这三个部分按反顺序压入栈中:
    stack.extend([i.right, i.val, i.left])

    • 为什么反着压?因为栈是“先进后出”,我们想要先处理左子树,所以左子树要最后压;

    • 此时节点值(i.val)是一个 int,等到下一次遍历时会被识别为“已展开节点”,可以直接记录结果。

▶️ 情况二:i 是一个整数(int
  • 说明它是某个节点在之前压栈时留下的 .val

  • 直接将它加入结果列表,相当于“真正访问”了该节点。


3️⃣ 遍历完成,返回结果

栈清空时,所有节点都已经以中序顺序访问,结果列表 rst 即为最终结果。


二、核心点总结

这段代码的关键在于:

将访问节点值与处理子节点的过程“解耦”,用类型判断区分是否“已展开”。

  • ✅ 借助栈顺序控制访问流程;

  • ✅ 用 isinstance(i, TreeNode) 来判断当前栈元素是否是“待处理节点”;

  • ✅ 中序遍历通过压入 [右子树, 当前值, 左子树] 的顺序来模拟递归。

这种技巧具有很高的统一性和可拓展性,适用于中序、前序、后序的迭代实现,只需要改变压栈顺序。

# 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]:
        stack, rst = [root], []  # 初始化栈和结果列表
        while stack:
            i = stack.pop()  # 弹出栈顶元素
            if isinstance(i, TreeNode):
                # 按照中序遍历的顺序,将右子节点、节点值、左子节点依次压入栈
                stack.extend([i.right, i.val, i.left])
            elif isinstance(i, int):
                # 如果节点值是 int 类型,输出其值
                rst.append(i)
        return rst

三、时间复杂度分析

每个节点:

  • 只会进入栈一次

  • 只会处理一次(包括子节点和节点值);

因此总操作与节点数量成正比:

时间复杂度:O(n),其中 n 为节点数。


四、空间复杂度分析

最坏情况下:

  • 所有节点都在一条边(退化为链表)时,栈深可达 n

  • 加上结果数组存储节点值,也为 O(n);

空间复杂度:O(n)

(注意:不考虑输出本身所占空间时,栈空间最坏 O(n),平均 O(log n) 对于平衡树)


✅ 总结一句话

这段代码通过“栈统一展开 + 类型判断延迟访问值”的策略,实现了逻辑清晰、结构统一的中序遍历。核心思想在于利用类型和压栈顺序来控制遍历流程,不再显式管理递归或访问标记。