之前讲完了二叉树的深度优先遍历---前序遍历、中序遍历、后序遍历, 这篇我们来讲一下广度优先遍历层序遍历
1. 回顾
深度优先遍历, 从根节点出发, 访问左右子树, 从上至下遍历整棵树
其中根据访问节点的顺序又可以分为
- 前序遍历 根左右
- 中序遍历 左根右
- 后序遍历 左右根
2. 何为层序遍历
顾名思义, 层序遍历按一层一层遍历整棵树, 从左到右遍历完当前层, 再往下遍历下一层

输出结果: [[10], [5, 20], [4, 6, 2, 8]]
用一个二维数组表示, 每一层再用一个数组表示
3. 数据结构
不同于深度遍历, 层序遍历需要把当前层访问完, 显然无法用栈这种数据结构实现, 栈是先把某一棵子树都访问完了, 再去访问另一棵子树, 不断递归子树, 然后再弹出返回上一层的形式无法满足遍历每一层。
层序遍历, 要求先访问左节点, 然后访问右节点, 然后输出左节点和右节点, 这种先进先出的形式我们用队列实现
4. 队列模拟过程
用queue来记录数据, 并维护queue.size()来确定哪些数据是同一层的.
一开始毫无疑问先把根节点10放入队列queue
只要queue不为空, 就记录size的值, 然后取值
每次取一个值出来, 记录到res里, 再把对应的左右节点放入队列, 同时size-=1
当之前的size为0时, 就意味着当前层遍历完了, 然后再次计算当前队列size, 如果队列为空, 说明整棵树遍历结束
把10放入队列

弹出10, 同时把10的left和right放入队列, size -1, 此时发现size =0, 说明当前层遍历完毕, 再次计算queue.size(), 然后更新size = 2

弹出5, 把5的左右节点放入队列, size - 1

弹出20, 把20的左右节点放入队列, size - 1

此时发现size = 0, 重新计算queue.size(), 发现为4, 弹出队首元素4, 没有左右节点, size - 1

弹出6, 没有左右节点, size - 1

弹出2, 没有左右节点, size - 1

弹出8, 没有左右节点, size - 1

发现size = 0, 说明当前层遍历完成, 又发现队列为空, 说明整棵树遍历结束, 返回结果
3. 关键
size只在为0时, 重新收集当前队列里的元素个数
每一次pop元素的时候, 就会把左右节点放入队列, 同时size - 1 , 这个时候就已经记录了下一层的元素, 当size为0, 说明这一层全部遍历结束, 同时下一层的所有元素已经放入队列, 即size控制了当前层的元素个数
4. 代码
# 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 levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
res = []
queue = []
if root: queue.append(root)
while queue:
temp = []
size = len(queue) # 在外面统计一次, 用size控制循环次数, 即当前层的遍历
while size:
node = queue[0]
queue.pop(0) # 弹出队列的第一个元素
temp.append(node.val)
if node.left: queue.append(node.left) # 添加左节点
if node.right: queue.append(node.right) # 添加右节点
size -=1
res.append(temp)
return res