🌳二叉树的层序遍历 广度优先

553 阅读8分钟

二叉树的层序遍历

102. 二叉树的层序遍历

Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, level by level).

Example 1:

Input: root = [3,9,20,null,null,15,7]

Output: [[3],[9,20],[15,7]]

Example 2:

Input: root = [1]

Output: [[1]]

Example 3:

Input: root = []

Output: []

Constraints:

  • The number of nodes in the tree is in the range [0, 2000].
  • -1000 <= Node.val <= 1000
class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []
        
        res = []
        queue = deque([root])

        while queue:
            tmp = []
            for i in range(len(queue)):
                node = queue.popleft()
                tmp.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.left:
                    queue.append(node.right)
            res.append(tmp)
        return res

image.png

image.png

可以得到二叉树每层的节点值组成的列表,返回的是一个二维列表。其中用到了 Python 的 deque 双端队列数据结构。

具体的实现过程如下:

  1. 首先判断根节点是否为空,如果为空则直接返回空列表。

  2. 初始化结果列表 res 和双端队列 queue,并将根节点添加到队列中。

  3. 进入循环,当队列不为空时执行以下操作:

    • 定义一个空列表 tmp,用于存储当前层的节点值。
    • 使用 for 循环遍历队列中当前层的节点,每次取出队列左侧的节点,并将其值添加到 tmp 中。
    • 如果当前节点有左子节点,则将左子节点添加到队列中。
    • 如果当前节点有右子节点,则将右子节点添加到队列中。
    • tmp 添加到 res 中。
  4. 返回结果列表 res

需要注意的是,在添加右子节点时,代码中写错了一个地方,应该是 if node.right:,而不是 if node.left:

这段代码实现了一种常见的二叉树遍历方式,即层序遍历。它可以帮助我们更加直观地了解二叉树的结构,以及在某些场景下可以发挥比其他遍历方式更好的性能。

在这是用的python的deque,dequequeue 都提供了删除队列左侧元素的方法,但是它们的实现方式是不同的。

deque 中,使用 popleft() 方法来删除左侧元素,它的时间复杂度是 O(1),因为 deque 内部是使用双向链表实现的,所以在左侧弹出元素的操作是很快的。

而在 queue 中,使用 pop(0) 方法来删除左侧元素,它的时间复杂度是 O(n),因为 queue 内部是使用列表实现的,当删除左侧元素时,需要把整个列表向左移动一位,这个操作的时间复杂度是与列表长度 n 相关的。

所以,如果需要频繁地删除队列左侧的元素,建议使用 deque。如果只是偶尔需要删除队列左侧的元素,使用 queue 也可以,但是要注意它的时间复杂度可能会影响性能。

扩展

103. 二叉树的锯齿形层序遍历

Given the root of a binary tree, return the zigzag level order traversal of its nodes' values. (i.e., from left to right, then right to left for the next level and alternate between).

Example 1:

Input: root = [3,9,20,null,null,15,7]

Output: [[3],[20,9],[15,7]]

Example 2:

Input: root = [1]

Output: [[1]]

Example 3:

Input: root = []

Output: []

Constraints:

  • The number of nodes in the tree is in the range [0, 2000].
  • -100 <= Node.val <= 100

这题和上一题代码就是多了一个even判断当前是奇数还是偶数。

函数zigzagLevelOrder接受一个二叉树作为输入,并返回一个列表,其中每个子列表表示二叉树的一个层次。列表中的每个子列表都按照锯齿形遍历的方式填充,即在偶数层上从左到右遍历,在奇数层上从右到左遍历。该函数使用一个队列来按照层次遍历二叉树,同时使用一个标志变量来确定当前层是奇数层还是偶数层。

# 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 zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root: 
            return []

        res = []
        queue = deque([root])
        even = False

        while queue:
            tmp = []
            for i in range(len(queue)):
                node = queue.popleft()
                tmp.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            res.append(tmp[::-1] if even else tmp)
            even = not even 
        return res

实现过程如下:

  1. 首先判断输入的根节点是否为空。如果为空,则直接返回空列表。

  2. 创建一个双端队列queue,并将根节点加入队列中。

  3. 创建一个布尔变量even,表示当前遍历的层数是否是偶数层。初始值为False

  4. 使用循环遍历队列queue中的所有节点:

    • 对于每个节点,将其值添加到临时列表tmp中,并将其左右子节点加入队列中。
    • 遍历完当前层的所有节点后,将临时列表tmp加入到结果列表res中。如果当前层为偶数层,则将tmp列表翻转后再加入res中。
    • 切换布尔变量even的值,以便在下一层遍历时按照相反的顺序排列节点值。
  5. 返回结果列表res

513. 找树左下角的值

Given the root of a binary tree, return the leftmost value in the last row of the tree.

Example 1:

Input: root = [2,1,3]

Output: 1

Example 2:

Input: root = [1,2,3,4,null,5,6,null,null,7]

Output: 7

Constraints:

  • The number of nodes in the tree is in the range [1,104][1, 10^4].
  • 231<=Node.val<=2311-2^{31} <= Node.val <= 2^{31} - 1

这个写起来就更简单了,不需要遍历,只保留底层最左边的结点,那直接把从左到右层序遍历,变为从右到左层序遍历即可,也就是

                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)

换成

            if node.right:
                queue.append(node.right)
            if node.left:
                queue.append(node.left)

完整代码

class Solution:
    def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        queue = deque([root])
        while queue:
            node = queue.popleft()
            if node.right:
                queue.append(node.right)
            if node.left:
                queue.append(node.left)
            res = node.val
        return res

实现过程如下:

  1. 创建一个双端队列queue,并将根节点加入队列中。

  2. 循环遍历队列queue中的所有节点,每次取出队列中的第一个节点:

    • 如果该节点的右子节点不为空,则将右子节点加入队列中。
    • 如果该节点的左子节点不为空,则将左子节点加入队列中。
    • 记录当前节点的值为结果res
  3. 遍历完整棵树后,返回结果res即可。

199. 二叉树的右视图

给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

 

示例 1:

输入: [1,2,3,null,5,null,4]
输出: [1,3,4]

示例 2:

输入: [1,null,3]
输出: [1,3]

示例 3:

输入: []
输出: []

提示:

  • 二叉树的节点个数的范围是 [0,100]
  • -100 <= Node.val <= 100

还是层序遍历,最右边的视图也就是层序遍历每层的最后一个节点。

写法1:

这里是用的tmp[-1],比较好理解,每层遍历之后的最后一个节点值。

# 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 rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []

        res = []
        queue = deque([root])

        while queue:
            tmp = []
            for i in range(len(queue)):
                node = queue.popleft()
                tmp.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            res.append(tmp[-1])
        
        return res

简化版的代码如下,结果也是对的,node在每次for循环之后也是指向当前层最后一个节点的,所以直接用node.val也是对的。

# 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 rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []

        res = []
        queue = deque([root])

        while queue:
            for i in range(len(queue)):
                node = queue.popleft()
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            res.append(node.val)
        
        return res

实现过程如下:

  1. 首先判断输入的根节点是否为空。如果为空,则直接返回空列表。

  2. 创建一个空列表res和一个双端队列queue,并将根节点加入队列中。

  3. 使用循环遍历队列queue中的所有节点:

    • 对于每个节点,将其左右子节点加入队列中。
    • 当遍历到每层的最后一个节点时,将该节点的值加入结果列表res中。
  4. 遍历完整棵树后,返回结果列表res即可。

116/117. 填充每个节点的下一个右侧节点指针

116. 填充每个节点的下一个右侧节点指针 - 力扣(Leetcode)

117. 填充每个节点的下一个右侧节点指针 II - 力扣(Leetcode)

给定一个二叉树:

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL 。

初始状态下,所有 next 指针都被设置为 NULL 。

 

示例 1:

输入:root = [1,2,3,4,5,null,7] 输出: [1,#,2,3,#,4,5,7,#] 解释: 给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化输出按层序遍历顺序(由 next 指针连接),'#' 表示每层的末尾。

示例 2:

输入: root = [] 输出: []

 

提示:

  • 树中的节点数在范围 [0, 6000] 内
  • -100 <= Node.val <= 100

进阶:

  • 你只能使用常量级额外空间。
  • 使用递归解题也符合要求,本题中递归程序的隐式栈空间不计入额外空间复杂度。
class Solution:
    def connect(self, root: 'Node') -> 'Node':
        if not root:
            return
        queue = deque([root])
        while queue:
            l = len(queue)
            for i in range(l):
                node = queue.popleft()
                if i != l-1:
                    node.next = queue[0]
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
        return root

实现过程如下:

  1. 首先判断输入的根节点是否为空。如果为空,则直接返回空值。

  2. 创建一个双端队列queue,并将根节点加入队列中。

  3. 使用循环遍历队列queue中的所有节点:

    • 对于每个节点,如果它不是该层的最后一个节点,则将该节点的next指针指向该层的下一个节点,即队列中的第一个节点。
    • 将该节点的左右子节点加入队列中。
  4. 遍历完整棵树后,返回原始的根节点即可。

637. 二叉树的层平均值

给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。

 

示例 1:

输入: root = [3,9,20,null,null,15,7]
输出: [3.00000,14.50000,11.00000]
解释: 第 0 层的平均值为 3,第 1 层的平均值为 14.5,第 2 层的平均值为 11 。
因此返回 [3, 14.5, 11]

示例 2:

输入: root = [3,9,20,15,7]
输出: [3.00000,14.50000,11.00000]

 

提示:

  • 树中节点数量在 [1, 104] 范围内
  • -231 <= Node.val <= 231 - 1
class Solution:
    def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
        res = []
        queue = deque([root])
        while queue:
            tmp = []
            for i in range(len(queue)):
                node = queue.popleft()
                tmp.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            res.append(sum(tmp) / len(tmp))
        return res

代码使用一个队列来存储节点,从根节点开始,每次将队列中的节点依次出队,并将它们的值加入到一个临时列表tmp中。接着,将每个节点的左右子节点加入队列中。当队列中的节点全部遍历完成后,计算平均值并将其添加到结果列表res中,最后返回res即可。

代码的时间复杂度为O(n),其中n是二叉树的节点数。空间复杂度也为O(n),因为需要一个队列来存储节点。


本文正在参加「金石计划」