层序遍历(Level Order Traversal) 是二叉树最高频的算法考点之一,更是微软、字节等大厂面试的“必考题”。除了基础版本,面试官往往会要求写出至少2~3种变体。本文将以 6种层序遍历变体 为核心,手把手教你写出面试官无法挑剔的代码,并提供可直接复用的代码模板!
一、基础回顾:标准层序遍历
1.1 核心思想
使用 队列(Queue) 实现广度优先搜索(BFS),按层级输出节点值:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def level_order(root):
if not root:
return []
queue = [root]
result = []
while queue:
level = []
for _ in range(len(queue)): # 固定当前层的节点数量
node = queue.pop(0)
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(level)
return result
1.2 复杂度分析
- 时间复杂度:O(n),每个节点进出队列一次
- 空间复杂度:O(n),队列最大存储宽度节点数
二、变体1:之字形遍历(锯齿形层序)
2.1 题目场景
- LeetCode 103:二叉树的锯齿形层序遍历
- 要求:奇数层从左到右,偶数层从右到左输出
2.2 实现方案
通过 层级奇偶标记 控制反转方向:
def zigzag_level_order(root):
if not root:
return []
queue = [root]
result = []
reverse = False # 反转标记
while queue:
level = []
for _ in range(len(queue)):
node = queue.pop(0)
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(level[::-1] if reverse else level)
reverse = not reverse # 切换反转状态
return result
三、变体2:自底向上层序遍历
3.1 应用场景
- LeetCode 107:二叉树的层序遍历 II
- 要求:从叶子节点所在层开始,逐层向上遍历
3.2 实现技巧
在基础层序遍历后,直接 反转结果列表:
def level_order_bottom(root):
result = level_order(root) # 复用基础层序遍历
return result[::-1]
四、变体3:带层级分隔符的输出
4.1 面试需求
要求每层节点用特定符号分隔(如 #
),便于可视化展示:
输入:
1
/ \
2 3
输出:["1", "2#3"]
4.2 代码实现
def level_separator(root):
if not root:
return []
queue = [root]
result = []
while queue:
level = []
level_str = []
for _ in range(len(queue)):
node = queue.pop(0)
level_str.append(str(node.val))
if node.left:
queue.append(node.left)
level.append(node.left)
if node.right:
queue.append(node.right)
level.append(node.right)
result.append("#".join(level_str))
return result
五、变体4:统计每层节点数
5.1 进阶问题
- 问题示例:求二叉树的最大宽度(LeetCode 662)
- 技巧:记录每层第一个和最后一个节点的位置索引
5.2 带位置索引的层序遍历
def width_of_binary_tree(root):
if not root:
return 0
queue = [(root, 0)] # (节点, 位置索引)
max_width = 1
while queue:
level_size = len(queue)
first_pos = queue[0][1]
last_pos = queue[-1][1]
max_width = max(max_width, last_pos - first_pos + 1)
for _ in range(level_size):
node, pos = queue.pop(0)
if node.left:
queue.append((node.left, 2 * pos))
if node.right:
queue.append((node.right, 2 * pos + 1))
return max_width
六、变体5:右视图(输出每层最后一个节点)
6.1 高频考题
- LeetCode 199:二叉树的右视图
- 要求:从右侧视角能看到的节点值集合
6.2 实现方案
每层遍历时,仅记录最后一个节点:
def right_side_view(root):
if not root:
return []
queue = [root]
result = []
while queue:
level_size = len(queue)
for i in range(level_size):
node = queue.pop(0)
if i == level_size - 1: # 每层最后一个节点
result.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return result
七、变体6:N叉树的层序遍历
7.1 扩展场景
当树结构从二叉树变为N叉树时,需遍历所有子节点:
class NTreeNode:
def __init__(self, val=None, children=None):
self.val = val
self.children = children or []
def nary_level_order(root):
if not root:
return []
queue = [root]
result = []
while queue:
level = []
for _ in range(len(queue)):
node = queue.pop(0)
level.append(node.val)
for child in node.children: # 遍历所有子节点
queue.append(child)
result.append(level)
return result
八、面试技巧与高频追问
8.1 代码注意事项
- 空节点处理:始终检查
node.left/node.right
是否存在 - 队列实现选择:Python中用
deque
更高效(本文用列表简化理解) - 层级固定技巧:
for _ in range(len(queue))
是关键
8.2 高频追问与回答
-
Q1:如何用DFS实现层序遍历?
A1:递归时记录当前深度,按层级存储节点(空间复杂度O(h),h为树高) -
Q2:如何统计每层的平均值/最大值?
A2:遍历每层时计算累加值或比较最大值 -
Q3:如果要求输出每层的第一个节点(左视图)?
A3:记录每层的第一个节点(与右视图对称)
九、性能对比与总结
变体类型 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|
基础层序遍历 | O(n) | O(w) | 常规层级输出 |
之字形遍历 | O(n) | O(w) | 交替方向输出 |
右视图/左视图 | O(n) | O(w) | 边缘节点可视化 |
最大宽度统计 | O(n) | O(w) | 树的结构分析 |
N叉树层序遍历 | O(n) | O(w) | 多子节点树结构 |
相关LeetCode练习题: