什么是二叉树?
二叉树是一种树形数据结构,每个节点最多有两个子节点,通常称为左子节点和右子节点。
常见的二叉树类型
- 完全二叉树: 除了最后一层,其他层的节点都是满的,最后一层的节点都靠左排列。堆就是用完全二叉树实现的。
- 满二叉树: 每一层的节点数都达到最大值,即每层都是满的。如果深度为 k,则有 2^k - 1 个节点。
- 二叉搜索树(BST): 对于任意节点,左子树的所有节点值都小于该节点值,右子树的所有节点值都大于该节点值。这个性质使得 BST 的查找、插入、删除操作都很高效。
- 平衡二叉树: 任意节点的左右子树高度差不超过 1。常见的实现有 AVL 树和红黑树,用于保证树的操作时间复杂度。
二叉树的表示和构建
节点定义
在 LeetCode 和大多数面试中,二叉树节点的定义都是标准的:
1 2 3 4 5
| class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
|
手动构建二叉树
面试或调试时,我们经常需要手动构建测试用例。最直接的方式是逐个创建节点:
1 2 3 4 5 6 7 8 9 10 11 12
|
root = TreeNode(1) root.left = TreeNode(2) root.right = TreeNode(3) root.left.left = TreeNode(4) root.left.right = TreeNode(5)
|
这种方式很直观,但对于稍大一点的树就显得繁琐。
从列表构建二叉树
LeetCode 上的题目通常用列表表示二叉树,比如 [1,2,3,null,null,4,5]
。这是层次遍历的表示方式,null 表示空节点。从层次遍历的表示构建二叉树如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| from collections import deque
def build_tree(values): """从列表构建二叉树(层次遍历顺序)""" if not values: return None
root = TreeNode(values[0]) queue = deque([root]) i = 1
while queue and i < len(values): node = queue.popleft()
if i < len(values) and values[i] is not None: node.left = TreeNode(values[i]) queue.append(node.left) i += 1
if i < len(values) and values[i] is not None: node.right = TreeNode(values[i]) queue.append(node.right) i += 1
return root
root = build_tree([1, 2, 3, None, None, 4, 5])
|
二叉树解题核心思想
- 信息从哪里来?(自顶向下还是自底向上)
- 信息如何传递?(通过参数还是返回值)
- 什么时候更新答案?(遍历过程中还是递归返回时)
根据这三个维度,可以总结出几种常见的解题模板。
模板一:自顶向下遍历
适用场景
当问题需要从根节点向下传递信息时使用。通常的特征是:
- 答案与”从根到当前节点的路径”有关
- 需要记录路径上的某些统计信息(和、最大值、最小值等)
- 涉及祖先和后代节点的关系
典型例题:
- Path Sum 系列
- Binary Tree Paths
- Maximum Difference Between Node and Ancestor
代码框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Solution: def solve(self, root): self.result = initial_value self.dfs(root, initial_path_info) return self.result
def dfs(self, node, path_info): """ 参数说明: node: 当前节点 path_info: 从根到当前节点的路径信息 """ if not node: return
current_result = process(node.val, path_info) self.result = update(self.result, current_result)
new_path_info = update_path_info(path_info, node.val)
self.dfs(node.left, new_path_info) self.dfs(node.right, new_path_info)
|
实例:Maximum Difference Between Node and Ancestor
这道题要求找出树中任意节点与其祖先节点的最大差值。思路是在遍历过程中维护路径上的最大值和最小值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Solution: def maxAncestorDiff(self, root): return self.dfs(root, root.val, root.val)
def dfs(self, node, path_max, path_min): if not node: return path_max - path_min
path_max = max(path_max, node.val) path_min = min(path_min, node.val)
left = self.dfs(node.left, path_max, path_min) right = self.dfs(node.right, path_max, path_min)
return max(left, right)
|
关键点:
- 通过参数向下传递路径上的最大最小值
- 在叶子节点返回当前路径的差值
- 递归过程中取所有路径的最大值
实例:Path Sum
判断是否存在从根到叶子的路径,使得路径和等于目标值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Solution: def hasPathSum(self, root, targetSum): if not root: return False return self.dfs(root, 0, targetSum)
def dfs(self, node, path_sum, target): if not node: return False
path_sum += node.val
if not node.left and not node.right: return path_sum == target
return (self.dfs(node.left, path_sum, target) or self.dfs(node.right, path_sum, target))
|
实例:Binary Tree Paths
返回所有从根到叶子的路径。需要注意回溯的使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Solution: def binaryTreePaths(self, root): result = []
def dfs(node, path): if not node: return
path.append(str(node.val))
if not node.left and not node.right: result.append('->'.join(path)) else: dfs(node.left, path) dfs(node.right, path)
path.pop()
dfs(root, []) return result
|
模板二:自底向上递归
适用场景
当需要利用子树的信息来计算当前节点的答案时使用。特征包括:
- 需要先知道左右子树的某些统计信息
- 问题可以分解为子问题
- 典型的”后序遍历”思想
典型例题:
- Maximum Depth of Binary Tree
- Balanced Binary Tree
- Diameter of Binary Tree
- Binary Tree Maximum Path Sum
代码框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Solution: def solve(self, root): self.result = initial_value self.dfs(root) return self.result
def dfs(self, node): """ 返回值:当前子树的某种统计信息 """ if not node: return base_case
left_info = self.dfs(node.left) right_info = self.dfs(node.right)
current_info = compute(node.val, left_info, right_info)
self.result = update(self.result, current_info)
return info_for_parent(current_info)
|
实例:Maximum Depth
最基础的自底向上问题。
1 2 3 4 5 6 7 8 9
| class Solution: def maxDepth(self, root): if not root: return 0
left_depth = self.maxDepth(root.left) right_depth = self.maxDepth(root.right)
return max(left_depth, right_depth) + 1
|
实例:Binary Tree Maximum Path Sum
这是一道 Hard 题,但用自底向上的思路就很清晰。关键在于区分”返回给父节点的值”和”全局最大值”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Solution: def maxPathSum(self, root): self.max_sum = float('-inf')
def dfs(node): if not node: return 0
left_gain = max(dfs(node.left), 0) right_gain = max(dfs(node.right), 0)
current_path_sum = node.val + left_gain + right_gain self.max_sum = max(self.max_sum, current_path_sum)
return node.val + max(left_gain, right_gain)
dfs(root) return self.max_sum
|
核心思想:
- 全局答案可以是”左子路径 + 根 + 右子路径”
- 但返回给父节点时只能选择一边
实例:Diameter of Binary Tree
直径是任意两节点间最长路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Solution: def diameterOfBinaryTree(self, root): self.diameter = 0
def dfs(node): if not node: return 0
left_depth = dfs(node.left) right_depth = dfs(node.right)
self.diameter = max(self.diameter, left_depth + right_depth)
return max(left_depth, right_depth) + 1
dfs(root) return self.diameter
|
模板三:层次遍历(BFS)
适用场景
当需要按层处理节点时使用:
- 需要知道节点所在的层级
- 每层要做特殊处理
- 锯齿形遍历等变种
典型例题:
- Binary Tree Level Order Traversal
- Binary Tree Zigzag Level Order Traversal
- Binary Tree Right Side View
- Average of Levels in Binary Tree
代码框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| from collections import deque
class Solution: def levelOrder(self, root): if not root: return []
result = [] queue = deque([root])
while queue: level_size = len(queue) current_level = []
for _ in range(level_size): node = queue.popleft() current_level.append(node.val)
if node.left: queue.append(node.left) if node.right: queue.append(node.right)
result.append(current_level)
return result
|
实例:Binary Tree Zigzag Level Order Traversal
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Solution: def zigzagLevelOrder(self, root): if not root: return []
result = [] queue = deque([root]) left_to_right = True
while queue: level_size = len(queue) current_level = deque()
for _ in range(level_size): node = queue.popleft()
if left_to_right: current_level.append(node.val) else: current_level.appendleft(node.val)
if node.left: queue.append(node.left) if node.right: queue.append(node.right)
result.append(list(current_level)) left_to_right = not left_to_right
return result
|
实例:Binary Tree Right Side View
返回从右侧看到的节点值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Solution: def rightSideView(self, root): if not root: return []
result = [] queue = deque([root])
while queue: level_size = len(queue)
for i in range(level_size): node = queue.popleft()
if i == level_size - 1: result.append(node.val)
if node.left: queue.append(node.left) if node.right: queue.append(node.right)
return result
|
模板四:前序遍历
适用场景
需要先访问根节点的场景:
代码框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| class Solution: def preorderTraversal(self, root): result = []
def dfs(node): if not node: return
result.append(node.val) dfs(node.left) dfs(node.right)
dfs(root) return result
def preorderTraversal_iterative(self, root): if not root: return []
result = [] stack = [root]
while stack: node = stack.pop() result.append(node.val)
if node.right: stack.append(node.right) if node.left: stack.append(node.left)
return result
|
模板五:中序遍历
适用场景
这个模板主要用于 BST 相关问题,因为中序遍历 BST 会得到有序序列。
典型例题:
- Validate Binary Search Tree
- Kth Smallest Element in a BST
- Binary Search Tree Iterator
代码框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| class Solution: def inorderTraversal(self, root): result = []
def dfs(node): if not node: return
dfs(node.left) result.append(node.val) dfs(node.right)
dfs(root) return result
def inorderTraversal_iterative(self, root): result = [] stack = [] current = root
while current or stack: while current: stack.append(current) current = current.left
current = stack.pop() result.append(current.val)
current = current.right
return result
|
实例:Kth Smallest Element in a BST
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Solution: def kthSmallest(self, root, k): self.count = 0 self.result = None
def inorder(node): if not node or self.result is not None: return
inorder(node.left)
self.count += 1 if self.count == k: self.result = node.val return
inorder(node.right)
inorder(root) return self.result
|
模板六:后序遍历
适用场景
需要先处理子节点再处理根节点的场景:
- 删除节点
- 计算表达式树
- 某些需要”自底向上”信息的场景
代码框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class Solution: def postorderTraversal(self, root): result = []
def dfs(node): if not node: return
dfs(node.left) dfs(node.right) result.append(node.val)
dfs(root) return result
def postorderTraversal_iterative(self, root): if not root: return []
result = [] stack = [root]
while stack: node = stack.pop() result.append(node.val)
if node.left: stack.append(node.left) if node.right: stack.append(node.right)
return result[::-1]
|
练习技巧
如何选择合适的模板
graph TD
A[遇到二叉树问题] --> B{是否需要层级信息?}
B -- 是 --> BFS[层次遍历(BFS)]
B -- 否 --> C{是否是 BST?}
C -- 是 --> INORDER[中序遍历(BST)]
C -- 否 --> D{信息流向?}
D -- 子树信息(自底向上) --> POSTORDER[后序遍历/自底向上]
D -- 路径信息(自顶向下) --> TOPDOWN[自顶向下 DFS]
D -- 其他 --> E{遍历顺序?}
E -- 先访问根 --> PREORDER[前序遍历]
E -- 先访问子节点 --> POSTORDER2[后序遍历]
自顶向下 + 回溯
适用于需要记录完整路径的场景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| def pathSum(root, target): result = []
def dfs(node, path, path_sum): if not node: return
path.append(node.val) path_sum += node.val
if not node.left and not node.right and path_sum == target: result.append(path[:])
dfs(node.left, path, path_sum) dfs(node.right, path, path_sum)
path.pop()
dfs(root, [], 0) return result
|
自底向上 + 全局变量
适用于需要记录全局最优解的场景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Solution: def longestUnivaluePath(self, root): self.max_length = 0
def dfs(node): if not node: return 0
left_len = dfs(node.left) right_len = dfs(node.right)
left = left_len + 1 if node.left and node.left.val == node.val else 0 right = right_len + 1 if node.right and node.right.val == node.val else 0
self.max_length = max(self.max_length, left + right)
return max(left, right)
dfs(root) return self.max_length
|
DFS + 剪枝
在搜索过程中提前排除不可能的分支。
1 2 3 4 5 6 7 8 9 10 11 12
| def isValidBST(root): def dfs(node, min_val, max_val): if not node: return True
if node.val <= min_val or node.val >= max_val: return False
return (dfs(node.left, min_val, node.val) and dfs(node.right, node.val, max_val))
return dfs(root, float('-inf'), float('inf'))
|
解题通用步骤
- 识别题目类型(路径、子树、层级、BST 等)
- 确定信息流向(自顶向下、自底向上、层序)
- 设计递归函数签名(参数和返回值)
- 处理边界条件(空节点、叶子节点)
- 实现核心逻辑
- 用小例子验证
推荐练习
自顶向下系列:
- Path Sum (Easy)
- Path Sum II (Medium)
- Binary Tree Paths (Easy)
- Maximum Difference Between Node and Ancestor (Medium)
自底向上系列:
- Maximum Depth of Binary Tree (Easy)
- Balanced Binary Tree (Easy)
- Diameter of Binary Tree (Easy)
- Binary Tree Maximum Path Sum (Hard)
BFS 系列:
- Binary Tree Level Order Traversal (Medium)
- Binary Tree Zigzag Level Order Traversal (Medium)
- Binary Tree Right Side View (Medium)
BST 系列:
- Validate Binary Search Tree (Medium)
- Kth Smallest Element in a BST (Medium)
- Binary Search Tree Iterator (Medium)