帕先生之二叉树层序遍历

102 阅读3分钟

之前讲完了二叉树的深度优先遍历---前序遍历、中序遍历、后序遍历, 这篇我们来讲一下广度优先遍历层序遍历

1. 回顾

深度优先遍历, 从根节点出发, 访问左右子树, 从上至下遍历整棵树

其中根据访问节点的顺序又可以分为

  • 前序遍历 根左右
  • 中序遍历 左根右
  • 后序遍历 左右根

2. 何为层序遍历

顾名思义, 层序遍历按一层一层遍历整棵树, 从左到右遍历完当前层, 再往下遍历下一层

binarytree.png

输出结果: [[10], [5, 20], [4, 6, 2, 8]]

用一个二维数组表示, 每一层再用一个数组表示

3. 数据结构

不同于深度遍历, 层序遍历需要把当前层访问完, 显然无法用这种数据结构实现, 是先把某一棵子树都访问完了, 再去访问另一棵子树, 不断递归子树, 然后再弹出返回上一层的形式无法满足遍历每一层。

层序遍历, 要求先访问左节点, 然后访问右节点, 然后输出左节点和右节点, 这种先进先出的形式我们用队列实现

4. 队列模拟过程

queue来记录数据, 并维护queue.size()来确定哪些数据是同一层的.

一开始毫无疑问先把根节点10放入队列queue

只要queue不为空, 就记录size的值, 然后取值

每次取一个值出来, 记录到res里, 再把对应的左右节点放入队列, 同时size-=1

当之前的size0时, 就意味着当前层遍历完了, 然后再次计算当前队列size, 如果队列为空, 说明整棵树遍历结束

把10放入队列

ss

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

s1s

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

s1s

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

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

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

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

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

发现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