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 上:
-
mean是statistics模块的一部分,而 LeetCode 自动为用户导入了这个模块,可以直接使用mean()。 - 在 LeetCode 上,很多常用模块(比如
statistics、math、collections等)都已经预先导入,用户可以直接使用它们。
在本地环境中:
如果在本地运行代码,需要先导入 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)
注意,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点。
总结:二叉树的层序遍历,就是图论中的广度优先搜索在二叉树中的应用,需要借助队列来实现。
浙公网安备 33010602011771号