文章目录
- [94. 二叉树的中序遍历](#94. 二叉树的中序遍历)
94. 二叉树的中序遍历
题目描述
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
示例 1:

输入:root = [1,null,2,3]
输出:[1,3,2]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]
提示:
- 树中节点数目在范围 [0, 100] 内
- -100 <= Node.val <= 100
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
解题思路
问题深度分析
本题要求输出二叉树的中序遍历序列(Left -> Root -> Right)。属于二叉树遍历的基础题,但可扩展为多种实现:递归、迭代(显式栈)、统一迭代(颜色标记)、以及 Morris 遍历(O(1) 额外空间)。
- 中序本质:访问顺序为左子树 -> 根节点 -> 右子树。
- 难点:在不使用递归的情况下如何"回溯"(用栈或临时线索)。
核心方法对比
- 方法一(递归):最直观,代码最短,空间为递归栈O(h)。
- 方法二(显式栈迭代):用栈模拟系统递归栈,通用稳定。
- 方法三(颜色标记统一迭代):将"访问/展开"统一成一个模板,适合多种遍历统一写法。
- 方法四(Morris 遍历):利用线索二叉树思想,空间O(1),不需要栈与递归,但会暂时修改树指针(遍历过程中会复原)。
算法流程图
递归(中序)
graph TD
    A[Inorder(root)] --> B{root==nil?}
    B -->|是| C[return]
    B -->|否| D[Inorder(root.Left)]
    D --> E[输出 root.Val]
    E --> F[Inorder(root.Right)]
显式栈迭代
是 是 否 否 stack空, cur=root cur!=nil or stack不空? cur!=nil? 压栈cur, cur=cur.Left 出栈node 输出node.Val cur=node.Right 结束
Morris 遍历
否 是 是 否 cur=root cur是否有左子树? 输出cur.Val, cur=cur.Right pre=cur.Left的最右节点 pre.Right==nil? pre.Right=cur, cur=cur.Left pre.Right=nil, 输出cur.Val, cur=cur.Right
复杂度分析
- 时间复杂度:四种方法均为 O(n),n 为节点数。
- 空间复杂度:
- 递归:O(h) 递归栈
- 显式栈/颜色标记:O(h) 栈空间
- Morris:O(1) 额外空间(会临时建立和拆除线索)
 
关键边界与陷阱
- 空树:返回空切片。
- 只有一个节点:直接返回该值。
- 极度不平衡树(链状):递归可能接近最坏栈深;Morris优势明显。
- Morris一定要"复原"pre.Right,否则破坏原树结构。
方法与要点
- 递归:模板清晰,先左后根再右。
- 栈迭代:while(cur!=nil)先一路向左入栈;否则出栈访问,再转向右。
- 颜色标记:节点入栈两次,第一次展开(入右、入自身白、入左),第二次(黑)访问。
- Morris:寻找左子树最右节点pre;首遇建立线索,二遇拆线索并访问根。
测试用例设计
- 
1,null,2,3\] -\> \[1,3,2 
- 
\] -\> \[ 
- 
1\] -\> \[1 
- 完全二叉树如 [4,2,6,1,3,5,7] -> [1,2,3,4,5,6,7]
- 退化链(全左/全右)
实战技巧
- Morris 空间O(1)是亮点,注意指针复原顺序。
- 颜色标记可统一前/中/后序实现,便于模板化。
- 递归实现最易读,作为基线版本很有用。
完整题解代码
            
            
              go
              
              
            
          
          package main
import (
	"fmt"
)
type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}
// =========================== 方法一:递归 ===========================
func inorderTraversal1(root *TreeNode) []int {
	var res []int
	var dfs func(*TreeNode)
	dfs = func(node *TreeNode) {
		if node == nil {
			return
		}
		dfs(node.Left)
		res = append(res, node.Val)
		dfs(node.Right)
	}
	dfs(root)
	return res
}
// =========================== 方法二:显式栈迭代 ===========================
func inorderTraversal2(root *TreeNode) []int {
	var res []int
	stack := []*TreeNode{}
	cur := root
	for cur != nil || len(stack) > 0 {
		for cur != nil {
			stack = append(stack, cur)
			cur = cur.Left
		}
		n := len(stack) - 1
		node := stack[n]
		stack = stack[:n]
		res = append(res, node.Val)
		cur = node.Right
	}
	return res
}
// =========================== 方法三:颜色标记统一迭代 ===========================
// 白色:0 表示展开;黑色:1 表示访问
func inorderTraversal3(root *TreeNode) []int {
	if root == nil {
		return nil
	}
	type item struct {
		node *TreeNode
		clr  int
	}
	const white, black = 0, 1
	var res []int
	stack := []item{{root, white}}
	for len(stack) > 0 {
		n := len(stack) - 1
		i := stack[n]
		stack = stack[:n]
		if i.node == nil {
			continue
		}
		if i.clr == white {
			// 中序:右 黑 自 黑 左 白
			stack = append(stack, item{i.node.Right, white})
			stack = append(stack, item{i.node, black})
			stack = append(stack, item{i.node.Left, white})
		} else {
			res = append(res, i.node.Val)
		}
	}
	return res
}
// =========================== 方法四:Morris 遍历 ===========================
func inorderTraversal4(root *TreeNode) []int {
	var res []int
	cur := root
	for cur != nil {
		if cur.Left == nil {
			res = append(res, cur.Val)
			cur = cur.Right
			continue
		}
		// 寻找前驱(左子树最右)
		pre := cur.Left
		for pre.Right != nil && pre.Right != cur {
			pre = pre.Right
		}
		if pre.Right == nil {
			pre.Right = cur
			cur = cur.Left
		} else {
			pre.Right = nil
			res = append(res, cur.Val)
			cur = cur.Right
		}
	}
	return res
}
// =========================== 构建/工具 ===========================
func arrayToTreeLevelOrder(arr []any) *TreeNode {
	// 基于层序队列的构建:按顺序为每个出队节点填充左/右孩子
	if len(arr) == 0 {
		return nil
	}
	if arr[0] == nil {
		return nil
	}
	root := &TreeNode{Val: arr[0].(int)}
	queue := []*TreeNode{root}
	i := 1
	for i < len(arr) && len(queue) > 0 {
		n := queue[0]
		queue = queue[1:]
		// 左孩子
		if i < len(arr) {
			if arr[i] != nil {
				left := &TreeNode{Val: arr[i].(int)}
				n.Left = left
				queue = append(queue, left)
			}
			i++
		}
		// 右孩子
		if i < len(arr) {
			if arr[i] != nil {
				right := &TreeNode{Val: arr[i].(int)}
				n.Right = right
				queue = append(queue, right)
			}
			i++
		}
	}
	return root
}
func equalSlice(a, b []int) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}
func buildRightChain(vals []int) *TreeNode {
	if len(vals) == 0 {
		return nil
	}
	root := &TreeNode{Val: vals[0]}
	cur := root
	for i := 1; i < len(vals); i++ {
		cur.Right = &TreeNode{Val: vals[i]}
		cur = cur.Right
	}
	return root
}
// =========================== 测试 ===========================
func main() {
	fmt.Println("=== LeetCode 94: 二叉树的中序遍历 ===\n")
	testCases := []struct {
		name     string
		root     *TreeNode
		expected []int
	}{
		{
			name:     "例1: [1,null,2,3]",
			root:     arrayToTreeLevelOrder([]any{1, nil, 2, 3}),
			expected: []int{1, 3, 2},
		},
		{
			name:     "空树",
			root:     arrayToTreeLevelOrder([]any{}),
			expected: []int{},
		},
		{
			name:     "单节点",
			root:     arrayToTreeLevelOrder([]any{1}),
			expected: []int{1},
		},
		{
			name:     "完全二叉树",
			root:     arrayToTreeLevelOrder([]any{4, 2, 6, 1, 3, 5, 7}),
			expected: []int{1, 2, 3, 4, 5, 6, 7},
		},
		{
			name:     "全左链",
			root:     arrayToTreeLevelOrder([]any{3, 2, nil, 1}),
			expected: []int{1, 2, 3},
		},
		{
			name:     "全右链",
			root:     buildRightChain([]int{1, 2, 3}),
			expected: []int{1, 2, 3},
		},
	}
	methods := map[string]func(*TreeNode) []int{
		"递归":         inorderTraversal1,
		"栈迭代":       inorderTraversal2,
		"颜色标记":      inorderTraversal3,
		"Morris O(1)": inorderTraversal4,
	}
	for name, f := range methods {
		fmt.Printf("方法:%s\n", name)
		pass := 0
		for i, tc := range testCases {
			got := f(tc.root)
			ok := equalSlice(got, tc.expected)
			status := "✅"
			if !ok {
				status = "❌"
			}
			fmt.Printf("  测试%d(%s): %s\n", i+1, tc.name, status)
			if !ok {
				fmt.Printf("    输出: %v\n    期望: %v\n", got, tc.expected)
			} else {
				pass++
			}
		}
		fmt.Printf("  通过: %d/%d\n\n", pass, len(testCases))
	}
}