LeetCode Day5 -- 二叉树 - 详解
目录
1. 啥时候用二叉树?
(1)典型问题
分层数据处理(文件系统、组织架构)
高效搜索(BST中查找/插入/删除)
路径问题(根节点到叶节点的路径)
树形结构操作(镜像、深度、平衡性判断)
表达式解析(语法树)
(2)核心思路
def solve(root):
if root is None: # 关键:始终先处理空节点
return base_value # 如深度为0,路径为空列表等
# 递归分解子问题
left_result = solve(root.left) # 处理左子树
right_result = solve(root.right) # 处理右子树
# 合并结果(根据问题类型选择策略)
return combine(root.val, left_result, right_result)
2. BFS、DFS、BST
2.1 广度优先搜索BFS
(1)适用任务
-- 层序遍历(按层级输出节点)
-- 最短路径(根节点到目标节点的最小深度)
-- 寻找最近邻节点
(2)解决思路:使用队列逐层遍历
from collections import deque
def bfs(root):
if not root: return []
queue = 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
2.2 深度优先搜索DFS
(1)三种变体(前中后序)
遍历方式 | 应用场景 | 解决任务 | 操作顺序 |
---|---|---|---|
前序遍历 | 节点初始化、路径记录、自顶向下操作 | 复制树、表达式前缀表示 | 根 → 左 → 右 |
中序遍历 | 二叉搜索树操作、顺序相关处理 | BST升序输出、表达式解析 | 左 → 根 → 右 |
后序遍历 | 子树信息统计、依赖子树结果的操作 | 子树统计、内存释放 | 左 → 右 → 根 |
关键判断依据:当前节点操作与子树操作的关系
如果操作依赖子树结果 → 用后序遍历(如:树深度、子树统计)
如果操作在前子树操作前必须完成 → 用前序遍历(如:路径记录、节点初始化)
如果涉及顺序处理 → 用中序遍历(如:BST中序遍历有序)
(2)解决思路:递归实现
""" 前序遍历 """
def preorder(root):
if not root: return
print(root.val) # 先操作根节点
preorder(root.left)
preorder(root.right)
""" 中序遍历 (BST关键) """
def inorder(root):
if not root: return
inorder(root.left)
print(root.val) # 在中间操作节点
inorder(root.right)
""" 后序遍历 """
def postorder(root):
if not root: return
postorder(root.left)
postorder(root.right)
print(root.val) # 最后操作根节点
2.3 二叉搜索树BST
左子树节点值 < 根节点值 < 右子树节点值
(1)典型任务
-- 查找元素(时间复杂度 O(log n))
-- 插入/删除节点(保持BST性质)
-- 范围查询(如找出值在 [low, high] 间的节点)
(2)代码模板
""" BST查找 """
def search(root, target):
while root:
if root.val == target: return True
root = root.left if target root.val:
root.right = insert(root.right, val)
return root # 返回更新后的子树根节点
""" 验证BST(中序遍历应用)"""
def isValidBST(root):
stack, prev = [], float('-inf')
while stack or root:
while root: # 深入左子树
stack.append(root)
root = root.left
root = stack.pop()
if root.val <= prev: return False # 破坏升序
prev = root.val
root = root.right # 转向右子树
return True
3. LeetCode
3.1 BFS
(1)199 二叉树的右视图
给定一个二叉树的 根节点root
,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
本质上就是二叉树每一层的最后一个入队的节点
from collections import deque
class Solution(object):
def rightSideView(self, root):
"""
:type root: Optional[TreeNode]
:rtype: List[int]
"""
if not root: return []
result=[]
quene = deque([root])
while quene:
size=len(quene)
for i in range(size):
node = quene.popleft()
if node.left: quene.append(node.left)
if node.right:quene.append(node.right)
cur_level_last = node.val
result.append(cur_level_last)
return result
(2)1161 最大层内元素和
给你一个二叉树的根节点 root
。设根节点位于二叉树的第 1
层,而根节点的子节点位于第 2
层,依此类推。请返回层内元素之和最大的那几层(可能只有一层)的层号,并返回其中最小的那个。
from collections import deque
class Solution(object):
def maxLevelSum(self, root):
"""
:type root: Optional[TreeNode]
:rtype: int
"""
if not root: return 0
quene = deque([root])
level=1
max_sum=[root.val, level]
while quene:
level_sum = 0
level += 1
for i in range(len(quene)):
node = quene.popleft()
level_sum += node.val
if node.left: quene.append(node.left)
if node.right:quene.append(node.right)
if max_sum[0]
3.2 DFS
(1)104 二叉树的最大深度
给定一个二叉树 root
,返回其最大深度。二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
DFS方法:后序遍历,先看左右子树的深度,更深的+1即为整棵树的最大深度。
class Solution(object):
def maxDepth(self, root):
"""
:type root: Optional[TreeNode]
:rtype: int
"""
if not root:
return 0
left_depth = self.maxDepth(root.left)
right_depth = self.maxDepth(root.right)
return max(left_depth,right_depth)+1
BFS方法:
from collections import deque
class Solution(object):
def maxDepth(self, root):
"""
:type root: Optional[TreeNode]
:rtype: int
"""
if not root:
return 0
quene = deque([root]) ## 队列中存的是当前层的所有节点
result = [] ## 用来保存每一层的节点
while quene:
cur_level = [] ## 当前层的节点
level_size = len(quene) ## 当前层有几个节点
for i in range(level_size):
node = quene.popleft()
cur_level.append(node.val) ## 将当前节点加入当前层中
if node.left:
quene.append(node.left)
if node.right:
quene.append(node.right)
result.append(cur_level)
return len(result)
(2)872 叶子相似的树
如果有两棵二叉树的叶值序列是相同,那么我们就认为它们是 叶相似 的。如果给定的两个根结点分别为 root1
和 root2
的树是叶相似的,则返回 true
;否则返回 false
。
DFS遍历都是先左后右的,因此均能保证叶子节点的顺序。
from collections import deque
class Solution(object):
def find_leaves(self,root,leaves):
if not root: return[]
left_leaves = self.find_leaves(root.left,leaves)
if not root.left and not root.right:
leaves.append(root.val)
right_leaves = self.find_leaves(root.right,leaves)
return leaves
def leafSimilar(self, root1, root2):
"""
:type root1: Optional[TreeNode]
:type root2: Optional[TreeNode]
:rtype: bool
"""
leaves1=[]
leaves2=[]
leaves1 = self.find_leaves(root1,leaves1)
leaves2 = self.find_leaves(root2,leaves2)
if leaves1==leaves2:
return True
else:
return False
(3)1448 统计二叉树中好节点的数目
给你一棵根为 root
的二叉树,请你返回二叉树中好节点的数目。「好节点」X 定义为:从根到该节点 X 所经过的节点中,没有任何节点的值大于 X 的值。
采用前序遍历(根 → 左 → 右)
根节点优先处理:在访问子节点前先判断当前节点,符合路径顺序
及时更新最大值:当遇到更大节点时,立即更新路径最大值传递给子节点
class Solution(object):
def dfs(self,root,cur_max):
if not root: return 0
count = 0
if root.val>=cur_max:
count=1
cur_max = root.val
count += self.dfs(root.left,cur_max)
count += self.dfs(root.right,cur_max)
return count
def goodNodes(self, root):
"""
:type root: Optional[TreeNode]
:rtype: int
"""
cur_max=-10001
return self.dfs(root,cur_max)
(4)1372 二叉树中的最长交错路径
给你一棵以 root
为根的二叉树,二叉树中的交错路径定义如下:
- 选择二叉树中 任意 节点和一个方向(左或者右)。
- 如果前进方向为右,那么移动到当前节点的的右子节点,否则移动到它的左子节点。
- 改变前进方向:左变右或者右变左。
- 重复第二步和第三步,直到你在树中无法继续移动。
交错路径的长度定义为:访问过的节点数目 - 1(单个节点的路径长度为 0 )。请你返回给定树中最长 交错路径 的长度。
使用后序遍历:①当前节点的状态计算依赖子节点状态
②需要先知道子节点的 left_path 和 right_path 才能计算当前节点
class Solution(object):
def longestZigZag(self, root):
"""
:type root: Optional[TreeNode]
:rtype: int
"""
self.max_path = 0
def dfs(node):
"""后序遍历返回(left_path, right_path)"""
"""left_path: 从当前节点出发,第一步向左走能形成的最长交错路径长度"""
"""right_path: 从当前节点出发,第一步向右走能形成的最长交错路径长度"""
if not node:
return (0, 0)
left_res = dfs(node.left)
right_res = dfs(node.right)
"""从当前节点向左走一步到左子节点(+1)
然后需要向右走,查询左子节点的向右走状态(right_path,即left_res[1])"""
left_path = 1 + left_res[1] if node.left else 0
"""从当前节点向右走一步到右子节点(+1)
然后需要向左走,查询右子节点向左走的状态(left_path,即right_res[0])"""
right_path = 1 + right_res[0] if node.right else 0
self.max_path = max(self.max_path, left_path, right_path)
return (left_path, right_path)
dfs(root)
return self.max_path
(5)236 二叉树的最近公共祖先
祖先可能的情况:
(1)p 和 q 分属不同子树 → 当前根节点是 LCA
(2)p 和 q 在同一子树 → LCA 在该子树中
(3)p 或 q 自身就是 LCA(祖孙关系)
→ 采用后序遍历顺序(左右根):
先深入子树寻找节点,再处理当前节点判断逻辑
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
if not root or root==p or root==q:
return root
left = self.lowestCommonAncestor(root.left,p,q) ## 在左子树中找到的p或q(或LCA)
right = self.lowestCommonAncestor(root.right,p,q) ## 在右子树中找到的p或q(或LCA)
if left and right: ## p和q分别在root的左右子树中
return root ## root是它们最低层级的共同祖先
if left: ## 两个节点必定都在左子树中
## 此时left要么是p和q中的一个(如果尚未找到两者关系),要么是p和q的LCA(如果已找到两者关系)
return left
return right
3.3 BST
(1)700 二叉搜索树中的搜索
给定二叉搜索树(BST)的根节点 root
和一个整数值 val
。你需要在 BST 中找到节点值等于 val
的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null
。
class Solution(object):
def searchBST(self, root, val):
"""
:type root: Optional[TreeNode]
:type val: int
:rtype: Optional[TreeNode]
"""
if not root: return null
while root:
if root.val==val:
return root
elif root.val
(2)450 删除二叉搜索树中的节点
要被删除的节点存在三种情况:
1. 叶子节点:直接删除(返回None)
2. 只有一个子节点:用子节点替代当前节点
实现:直接返回子节点是让父节点直接指向孙子节点
3. 有两个子节点:找到前置节点(当前节点左子树中最右侧节点)替换
实现:用前置节点值覆盖当前节点值 → 删除前置节点(递归调用)
class Solution(object):
def deleteNode(self, root, key):
"""
:type root: Optional[TreeNode]
:type key: int
:rtype: Optional[TreeNode]
"""
if not root: return None
if key>root.val: ## key比当前节点大,在右子树中删
root.right=self.deleteNode(root.right,key)
elif key
(3)98 验证二叉搜索树
方案一:递归实现
每个节点都有值范围约束:(low,high)
左子树继承上界:(low,node.val),右子树继承下界:(node.val,high)
class Solution(object):
def isValidBST(self, root):
"""
:type root: Optional[TreeNode]
:rtype: bool
"""
def validate(node, low=float('-inf'), high=float('inf')):
if not node:
return True
if node.val=high:
return False
is_left = validate(node.left, low, node.val)
is_right = validate(node.right, node.val, high)
return is_left and is_right
return validate(root)
方案二:中序遍历验证
使用栈模拟中序遍历过程,比较当前节点与前一个节点的值
class Solution(object):
def isValidBST(self, root):
stack=[]
pre=None
while stack or root:
""" 左 """
while root:
stack.append(root)
root=root.left
""" 根 """
root=stack.pop()
if pre is not None and root.val<=pre:
return False ## 当前值比pre值(其左侧)小
""" 右 """
pre=root.val
root=root.right
return True
(4)230 二叉搜索树中第k小的元素
因为搜索树的中序遍历得到一个递增序列,所以本质上就是求中序遍历的第k个值。
class Solution(object):
def kthSmallest(self, root, k):
"""
:type root: Optional[TreeNode]
:type k: int
:rtype: int
"""
stack=[]
count=0
while root or stack:
## 左
while root:
stack.append(root)
root=root.left
## 根
root=stack.pop()
count+=1
if count==k: ## 是否是第k个
return root.val
## 右
root=root.right