2025/8/28 二叉树的层序遍历

1. 102. 二叉树的层序遍历 - 力扣(LeetCode)

方法一:迭代+队列

class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []
        queue = collections.deque([root])
        result = []
        while queue:
            level = []
            for _ in range(len(queue)):
                node = queue.popleft()
                level.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            result.append(level)
        return result

知识点:

① python队列(Queue)

实现方式 数据结构 适用场景 是否线程安全
collections.deque 双端队列 常用,推荐
queue.Queue 标准队列 多线程通信时用
list 模拟队列 一般队列 小规模简单用法 否,性能低

(a)最常用:collections.deque

from collections import deque

queue = deque()

# 入队
queue.append(1)
queue.append(2)

# 出队
x = queue.popleft()  # FIFO:1 先出

print(x)         # 1
print(queue)     # deque([2])

append():尾部插入

appendleft():头部插入

pop():尾部弹出

popleft():头部弹出(✅ 队列常用)

len(queue) 会不会在每次循环中都重新计算?

for _ in range(len(queue)):

不会。

len(queue) 是在进入 range(...) 时就计算好了,range(...) 在整个 for 循环开始时只计算一次,range 生成的是一个固定的整数序列(迭代器)
后续即使 queue 变了,range(...) 里面的长度也不会变。

方法二:递归

class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []
        
        result = []

        def traverse(node, level):
            if not node:
                return
            
            if len(result) == level:
                result.append([])

            result[level].append(node.val)
            traverse(node.left, level+1)
            traverse(node.right, level+1)
        
        traverse(root, 0)
        return result

 2. 107. 二叉树的层序遍历 II - 力扣(LeetCode) ✅

列表反转知识点:2025/5/12 【二叉树】前中后序迭代遍历 LeetCode144, 94, 145 【√】 - axuu - 博客园

(1)最常见的三种列表反转方式

①方法1:使用切片[::-1]

特点:返回新列表,原列表不变

②方法2:使用内置函数reversed() + list()

lst = [1, 2, 3, 4]
reversed_lst = list(reversed(lst))

特点:返回新列表,原列表不变,可用于字符串、元组等。

s = "hello"
print("".join(reversed(s)))  # 'olleh'

③方法3:使用list.reverse() 方法(原地反转 )

lst = [1, 2, 3, 4]
lst.reverse()
print(lst)  # [4, 3, 2, 1]

特点:无返回值,就地反转原列表

错误写法:

lst = [1, 2, 3]
lst = lst.reverse()  # ❌ lst 会变成 None!

list.reverse()原地操作,不返回新列表,只能单独调用:

(2)三种方法的对比总结表

方法 是否返回新列表 是否修改原列表 返回类型 是否推荐
lst[::-1] ✅ 是 ❌ 否 list  
list(reversed(lst)) ✅ 是 ❌ 否 list  
lst.reverse() ❌ 否(返回 None) ✅ 是 None(就地修改) 原地修改,节省内存

(3)应用场景举例

①反转字符串(用切片或 reversed

s = "hello"
print(s[::-1])  # 'olleh'
print("".join(reversed(s)))  # 'olleh'

字符串不可变,不可原地反转

3. 199. 二叉树的右视图 - 力扣(LeetCode) ✅

代码1,在基本的层序遍历代码上做小小的修改

class Solution:
    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        queue = collections.deque([root])
        result = []
        while queue:
            level = []
            for _ in range(len(queue)):
                node = queue.popleft()
                level.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            result.append(level[-1])
        return result

代码2

class Solution:
    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        queue = collections.deque([root])
        right_view = []
        while queue:
            level_size = len(queue)
            for i in range(level_size):
                node = queue.popleft()
                
                if i == level_size-1:
                    right_view.append(node.val)

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

        return right_view

4. 637. 二叉树的层平均值 - 力扣(LeetCode)

知识点:

在 LeetCode 上:

  • meanstatistics 模块的一部分,而 LeetCode 自动为用户导入了这个模块,可以直接使用 mean()

  • 在 LeetCode 上,很多常用模块(比如 statisticsmathcollections 等)都已经预先导入,用户可以直接使用它们。

在本地环境中:

如果在本地运行代码,需要先导入 statistics 模块才能使用 mean()

import statistics

numbers = [1, 2, 3, 4, 5]
average = statistics.mean(numbers)  # 需要显式导入
print(average)  # 3.0

②python中求平均数(averages)的基本方法:使用sum()len()

numbers = [1, 2, 3, 4, 5]
average = sum(numbers) / len(numbers)
print(average)  # 3.0

注意:如果列表为空,直接计算 len(numbers) 会导致 ZeroDivisionError(除零错误)。需要做检查,确保列表不为空。

5. 429. N 叉树的层序遍历 - 力扣(LeetCode)

解法一:迭代+队列

class Solution:
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        if not root:
            return []

        result = []
        queue = collections.deque([root])

        while queue:
            level_size = len(queue)
            level = []

            for _ in range(level_size):
                node = queue.popleft()
                level.append(node.val)

                for child in node.children:
                    queue.append(child)
            
            result.append(level)
            
        return result

解法2:递归法

class Solution:
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        if not root:
            return []

        result = []

        def traverse(node, level):
            if not node:
                return

            if len(result) == level:
                result.append([])

            result[level].append(node.val)
            for child in node.children:
                traverse(child, level+1)

        traverse(root, 0)
        return result

知识点:

①N叉树的定义代码

class Node:
    def __init__(self, val: Optional[int] = None, children: Optional[List['Node']] = None):
        self.val = val
        self.children = children

②类型注解(Type Hint)

类型注解就是:给变量、函数参数和返回值标注“它是什么类型”。

类型注解 = 变量 + 类型提示,不会影响代码执行,只是对人和工具的提示。

children: Optional[List['Node']] = None

这是 Python 的 类型注解 + 默认值 组合写法

(a)对变量 children 添加类型注解,

(b)Optional[...]

typing 模块中的一个泛型类型,含义是:

可以是某种类型,也可以是 None。

即:

Optional[X] == Union[X, None]

所以:

Optional[List['Node']]

表示:

children 这个变量 要么是一个 Node 类型的列表要么就是 None(即没有子节点)。

Optional[str] 就是 str | None(可以是字符串,也可以是 None)

* 类型注解的一种类型1:联合类型(Union

from typing import Union

def parse(x: Union[int, str]) -> str:
    return str(x)

**类型注解的一种类型2:递归结构(如'Node')

class Node:
    def __init__(self, val: int, children: List['Node']):
        self.val = val
        self.children = children

 

(c)List['Node']

表示这是一个列表类型(List),每个元素都是 'Node' 类型。

·为什么要加引号 'Node'?因为 Node 这个类还没有定义完

·这是 前向引用(forward reference),用于在类体中引用自己。

(d)= None

这是 Python 中的默认参数值写法,表示:如果用户在创建对象时 没有显式传入 children 参数,那么默认就用 None

(e)总结该注解代码的全部含义:

变量 children 是一个 列表,里面的每个元素都是 Node 类型;

但也可以是 None(表示没有子节点);

如果不传这个参数,默认就是 None

③前向引用

def levelOrder(self, root: 'Node'):

(a)为啥 'Node' 外面要加引号?

回答:因为在函数定义里引用了 Node 类型,但是 Node 类可能还没定义好
所以加引号 'Node' 表示:“我现在先告诉你我要用 Node 类型,但我后面再定义它。”
这就叫做:前向引用(forward reference)

当类 相互引用 或 递归类型(即类内部引用自身) 时,需要使用前向引用。

(b)为什么不能直接写 root: Node

错误写法:

def levelOrder(self, root: Node):  # 这时候 Node 还没定义

在 Python 解释器还没有“看到”Node 类的定义前,它不知道 Node 是啥,就会报错。

正确写法:用引号包起来

def levelOrder(self, root: 'Node'):

这样写,'Node' 会被当作一个**“延迟字符串”**处理。

Python 会等整个程序都加载完之后,再把 'Node' 当作真正的类型来用。

可以理解成提前“打个招呼”:Node 我后面再解释,现在先用引号占个位。

6. 515. 在每个树行中找最大值 - 力扣(LeetCode)

知识点:

①python中表示正负无穷

max_val = float('-inf')

是在 Python 中定义一个初始值为“负无穷”的变量,常用于找最大值的初始化。

float('-inf') 是一个合法的 Python 浮点数(float)值,表示 负无穷大(-∞)。

neg_inf = float('-inf')  # -∞
pos_inf = float('inf')   # +∞

另外的表示:

import math

math.inf       # +∞
-math.inf      # -∞

② max() 函数是 Python 中最常用的内置函数之一

基本语法:

max(a, b, c, ...)
max(iterable)

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

 注意按照题意:

用户要实现的功能:连接每一层的相邻节点

如果用户按题目要求把树连接好了,由平台用一个特殊的层序遍历方式检查

解法一:迭代+队列

"""
# Definition for a Node.
class Node:
    def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next
"""

class Solution:
    def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
        if not root:
            return root

        queue = collections.deque([root])

        while queue:
            prev = None

            for _ in range(len(queue)):
                node = queue.popleft()

                if prev:
                    prev.next = node

                prev = node
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            # prev.next = None
            
        return root

对于每一层最后一个结点,根本不需要显式设置 .next = None,因为默认它就是 None。

解法2:完美二叉树下的递归解法(116)

class Solution:
    def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
        if not root or not root.left:
            return root
        root.left.next = root.right
        # 在完美二叉树中,把相邻两个父节点的孩子连接起来。
        if root.next:
            root.right.next = root.next.left
        self.connect(root.left)
        self.connect(root.right)
        return root

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

上面116题的代码可以解决。

116题是完美二叉树(每个节点都有两个孩子,整棵树是满的)

117题目是普通二叉树(可以是任意形状,非满、不平衡)

9. 104. 二叉树的最大深度 - 力扣(LeetCode)

10.111. 二叉树的最小深度 - 力扣(LeetCode)

注意,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点。

总结:二叉树的层序遍历,就是图论中的广度优先搜索在二叉树中的应用,需要借助队列来实现。

posted on 2025-08-28 23:00  axuu  阅读(9)  评论(0)    收藏  举报