题目描述
逐层返回节点值,每层一个数组,层内从左到右。
思路解析
直接拿队列出一个、入孩子,确实能不重不漏访问每个节点。可所有层的节点全混在一个队列里,你根本不知道 9 和 15 是不是同一层——结果摊平成一个大数组,达不到「每层一个数组」。
转折就一句话:每轮进 while 时,队列里剩的恰好是「完整的一层」(上一层把它们全推进来了)。所以先记下 size=len(q),本轮只处理这么多个;处理完,队列里新攒的又恰好是下一整层。用这个「快照长度」当尺子,层与层就自然切开了。
根入队:开局把根 3 放进队列。队列里现在 1 个,就是第一层。
第 1 层 · 锁定 size=1:进 while,先拍快照:size=len(q)=1。本轮只出 1 个,不管后面又塞进来多少。新建 level=[]。
第 1 层 · 出队 3:出队 3,收进 level=[3]。接着看它的孩子。
第 1 层 · 孩子入队 → 本层完:3 的左右孩子 9、20 入队。size 个(1 个)已处理完,本层定格 [3]。此刻队列 [9,20] 正好是完整的第二层。
第 2 层 · 锁定 size=2:再进 while,快照 size=2。本轮就出这 2 个,待会儿 20 的孩子塞进来也不算进本层。
第 2 层 · 出队 9(叶子):出队 9,收进 level=[9]。9 没有孩子,不入队——这就是「负例」:不是每个节点都会往队列里加东西。
第 2 层 · 出队 20 → 本层完:出队 20,收进 level=[9,20]。20 的孩子 15、7 入队。本层 size=2 个出完,定格 [9,20]。队列 [15,7] 又是完整的第三层。
第 3 层 · 出队 15、7:第三轮 size=2:出队 15、7,都是叶子、无孩子入队,收进 [15,7]。队列空了,while 退出。
完成:三层依次收集,结果 [[3], [9,20], [15,7]]。靠的全是每轮那句 size=len(q)。
凡是「按层处理 / 求每层信息 / 最短层数」都用 BFS:队列出一个入孩子、用 len(q) 切层。锯齿遍历、右视图、最小深度都是它的变体。
参考代码(Python)
1if not root: return []
2q, res = deque([root]), []
3while q:
4 level = []
5 for _ in range(len(q)): # 锁定这一层的个数
6 node = q.popleft(); level.append(node.val)
7 if node.left: q.append(node.left)
8 if node.right: q.append(node.right)
9 res.append(level)
10return res复杂度分析
- 时间复杂度:O(n) —— 每个节点进出队一次
- 空间复杂度:O(n) —— 队列最多装一层
套路模板
记住骨架:队列起头、for _ in range(len(q)) 锁层、出队处理、孩子入队。和多源 BFS(腐烂橘子)是同一个"按层"骨架。
1q = deque([root])
2while q:
3 for _ in range(len(q)): # 锁定当前层
4 node = q.popleft()
5 # 处理 node(收集值/记层)
6 if node.left: q.append(node.left)
7 if node.right: q.append(node.right)易错点
- 错误写法:不锁 len(q) 直接 while → 正确写法:for _ in range(len(q)) 切层(不锁长度就分不清层与层的边界)
- 错误写法:入队前不判空 → 正确写法:if node.left 才入队(把 None 入队会在出队取值时报错)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。