二叉树遍历
二叉树常见的遍历方式包括层序遍历、前序遍历、中序遍历和后序遍历等
层序遍历
层序遍历 从顶部到底部逐层遍历二叉树 并在每一层按照从左到右的顺序访问节点
层序遍历本质上属于广度优先遍历,也称广度优先搜索(BFS),它体现了一种“一圈一圈向外扩展”的逐层遍历方式
广度优先遍历通常借助“队列”来实现。队列遵循“先进先出”的规则,而广度优先遍历则遵循“逐层推进”的规则,两者背后的思想是一致的
func levelOrder(root *TreeNode) []any {
// 1. 初始化队列容器(使用双向链表实现)
// 示例:初始时队列为空 []
queue := list.New()
// 2. 将根节点加入队列尾部(如果是空树则加入nil)
// 示例:当root=1节点,队列变为[1]
queue.PushBack(root)
// 3. 初始化结果切片(长度0的动态数组)
// 示例:初始nums=[]
nums := make([]any, 0)
// 4. 主循环:当队列不为空时持续遍历
// 示例:初始队列非空(root=1时),进入循环
for queue.Len() > 0 {
// 5. 出队操作:
// a. Front()获取队列首元素(链表节点)
// b. Remove()移除该节点并返回值
// c. 类型断言(*TreeNode)转为树节点
// 示例:取出根节点1,队列变空[]
node := queue.Remove(queue.Front()).(*TreeNode)
// 6. 将当前节点值加入结果集
// 示例:nums从[]变为[1]
nums = append(nums, node.Val)
// 7. 处理左子节点:
// 示例:当node=1时,左子节点2存在 → 队列变为[2]
if node.Left != nil {
queue.PushBack(node.Left) // 加入队列尾部
}
// 8. 处理右子节点:
// 示例:当node=1时,右子节点3存在 → 队列变为[2,3]
if node.Right != nil {
queue.PushBack(node.Right) // 加入队列尾部(左节点之后)
}
}
// 9. 返回层级遍历结果
// 示例二叉树:
// 1
// / \
// 2 3
// / \
// 4 5
// 最终返回 [1,2,3,4,5]
return nums
}
树结构:
1
/ \
2 3
/ \ \
4 5 6
| 循环次数 | 当前出队节点 | 操作后队列状态 | 子节点入队 | 结果集变化 |
|---|---|---|---|---|
| 1 | 1 | [2,3] | 左2→右3 | [1]→[1] |
| 2 | 2 | [3,4,5] | 左4→右5 | [1]→[1,2] |
| 3 | 3 | [4,5,6] | 右6 | [1,2]→[1,2,3] |
| 4 | 4 | [5,6] | 无子节点 | [1,2,3]→[1,2,3,4] |
| 5 | 5 | [6] | 无子节点 | ...→[1,2,3,4,5] |
| 6 | 6 | [] | 无子节点 | ...→[1,2,3,4,5,6] |
前序、中序、后序遍历
前序、中序和后序遍历都属于深度优先遍历,也称深度优先搜索(DFS),它体现了一种“先走到尽头,再回溯继续”的遍历方式
深度优先遍历就像是绕着整棵二叉树的外围“走”一圈,在每个节点都会遇到三个位置,分别对应前序遍历、中序遍历和后序遍历
深度优先搜索通常基于递归实现
/* 前序遍历 */
func preOrder(node *TreeNode) {
// 基线条件:遇到空节点(叶子节点的子节点)则返回
// 示例:遍历到节点2的左子节点(nil)时直接返回
if node == nil {
return
}
// 访问优先级:根节点 -> 左子树 -> 右子树
// 示例:对于根节点1 -> 节点2 -> 节点4的访问顺序
nums = append(nums, node.Val) // 先记录当前节点值
preOrder(node.Left) // 递归遍历左子树
preOrder(node.Right) // 递归遍历右子树
}
/* 中序遍历 */
func inOrder(node *TreeNode) {
// 基线条件:空节点直接返回
// 示例:节点4的左子节点(nil)直接返回
if node == nil {
return
}
// 访问优先级:左子树 -> 根节点 -> 右子树
// 示例:对于节点2 -> 节点4 -> 节点5 -> 节点1 -> 节点3的访问顺序
inOrder(node.Left) // 先递归遍历左子树
nums = append(nums, node.Val) // 然后记录当前节点值
inOrder(node.Right) // 最后递归遍历右子树
}
/* 后序遍历 */
func postOrder(node *TreeNode) {
// 基线条件:空节点直接返回
// 示例:节点5的右子节点(nil)直接返回
if node == nil {
return
}
// 访问优先级:左子树 -> 右子树 -> 根节点
// 示例:对于节点4 -> 节点5 -> 节点2 -> 节点6 -> 节点3 -> 节点1的访问顺序
postOrder(node.Left) // 先递归遍历左子树
postOrder(node.Right) // 然后递归遍历右子树
nums = append(nums, node.Val) // 最后记录当前节点值
}