代码随想录第14天:(二叉树)
一、找树左下角的值(Leetcode 513)
递归法:
class Solution:
def findBottomLeftValue(self, root: TreeNode) -> int:
# 初始化最大深度为 -1,表示当前尚未遍历任何节点
# 初始化 result 为 None,最终将存储最左边的节点值
self.max_depth = -1
self.result = None
# 从根节点开始深度优先遍历,初始深度为 0
self.traversal(root, 0)
# 返回遍历过程中记录的最左边节点的值
return self.result
def traversal(self, node, depth):
# 如果当前节点为空,直接返回(递归结束条件)
if not node:
return
# 如果当前节点的深度比最大深度大,说明找到了一个新的最深层
# 更新最大深度和当前深度下最左边的节点值
if depth > self.max_depth:
self.max_depth = depth
self.result = node.val # 记录当前深度的最左边节点值
# 先递归遍历左子树,再遍历右子树
# 左子树会先被访问,因此最左边的节点会先被更新为 result
# 递归调用时,深度加 1
self.traversal(node.left, depth + 1)
# 同样,递归调用右子树,深度也加 1
self.traversal(node.right, depth + 1)
迭代法:
from collections import deque
class Solution:
def findBottomLeftValue(self, root):
# 如果树为空,返回 0
if root is None:
return 0
# 使用双端队列来实现队列操作,支持 O(1) 的 popleft 操作
queue = deque()
# 将根节点添加到队列中
queue.append(root)
# 记录最左边节点的值,初始为 0
result = 0
# 开始广度优先遍历(层序遍历)
while queue:
# 获取当前队列的大小,即当前层的节点数
size = len(queue)
# 遍历当前层的所有节点
for i in range(size):
# 从队列中弹出一个节点
node = queue.popleft()
# 如果是当前层的第一个节点,则更新 result 为该节点的值
if i == 0:
result = node.val
# 如果当前节点有左子节点,将左子节点加入队列
if node.left:
queue.append(node.left)
# 如果当前节点有右子节点,将右子节点加入队列
if node.right:
queue.append(node.right)
# 返回最左边的节点值(最底层最左的节点)
return result
二、路径总和I(Leetcode 112)
遍历的路线,并不要遍历整棵树,所以递归函数需要返回值,可以用bool类型表示。
class Solution:
# 递归辅助函数:遍历树的每一条路径
def traversal(self, cur: TreeNode, count: int) -> bool:
# 1. 如果当前节点是叶子节点,并且路径和等于0,返回 True
if not cur.left and not cur.right and count == 0: # 叶子节点且路径和为0
return True
# 2. 如果是叶子节点,但路径和不为0,返回 False
if not cur.left and not cur.right: # 叶子节点,但路径和不为0
return False
# 3. 否则,继续遍历左右子树
# 处理左子树
if cur.left: # 如果有左子树
count -= cur.left.val # 减去当前节点的值,递归检查左子树
if self.traversal(cur.left, count): # 递归调用左子树
return True # 如果左子树存在符合条件的路径,直接返回 True
count += cur.left.val # 回溯,撤销左子树的减法
# 处理右子树
if cur.right: # 如果有右子树
count -= cur.right.val # 减去当前节点的值,递归检查右子树
if self.traversal(cur.right, count): # 递归调用右子树
return True # 如果右子树存在符合条件的路径,直接返回 True
count += cur.right.val # 回溯,撤销右子树的减法
# 如果左右子树都没有符合的路径,返回 False
return False
# 主函数:判断从根节点到叶子节点是否有路径和为 targetSum
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if root is None: # 空树没有路径
return False
# 从根节点开始递归,路径和初始化为 targetSum - root.val
return self.traversal(root, targetSum - root.val)
三、路径总和II (Leetcode 113)
路径总和ii要遍历整个树,找到所有路径,所以递归函数不要返回值!
class Solution:
def __init__(self):
# 初始化结果列表和路径列表
self.result = [] # 存储所有符合条件的路径
self.path = [] # 存储当前路径
def traversal(self, cur, count):
# 1. 遇到叶子节点且路径和等于目标和
if not cur.left and not cur.right and count == 0:
# 如果是叶子节点,且路径和符合条件,记录当前路径
self.result.append(self.path[:]) # 将当前路径复制到结果中
return
# 2. 遇到叶子节点但路径和不为0,直接返回
if not cur.left and not cur.right:
return
# 3. 递归遍历左子树
if cur.left:
self.path.append(cur.left.val) # 将左子节点的值加入路径
count -= cur.left.val # 更新路径和
self.traversal(cur.left, count) # 递归遍历左子树
count += cur.left.val # 回溯:撤销对路径和的修改
self.path.pop() # 回溯:从路径中移除最后一个节点
# 4. 递归遍历右子树
if cur.right:
self.path.append(cur.right.val) # 将右子节点的值加入路径
count -= cur.right.val # 更新路径和
self.traversal(cur.right, count) # 递归遍历右子树
count += cur.right.val # 回溯:撤销对路径和的修改
self.path.pop() # 回溯:从路径中移除最后一个节点
return
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
# 清空结果和路径,以确保每次调用时数据是干净的
self.result.clear()
self.path.clear()
# 如果根节点为空,返回空结果
if not root:
return self.result
# 先将根节点的值加入路径
self.path.append(root.val)
# 从根节点开始递归,更新路径和为 targetSum - 根节点的值
self.traversal(root, targetSum - root.val)
# 返回所有符合条件的路径
return self.result