# 树与图参考 ## 二叉树 ### 核心概念 **二叉树**是一种层次化数据结构,每个节点最多有两个子节点(左子节点与右子节点)。 **关键属性**: - 每个节点最多有 2 个子节点 - 根节点没有父节点 - 叶节点没有子节点 - 高度:从根节点到叶节点的最长路径 - 深度:从根节点到某节点的距离 **二叉树类型**: - **满二叉树**:每个节点有 0 或 2 个子节点 - **完全二叉树**:除最后一层外,所有层都被填满,且最后一层从左向右填充 - **完美二叉树**:所有内部节点都有 2 个子节点,所有叶节点在同一层 - **平衡二叉树**:左右子树的高度差 ≤ 1 ### 节点结构 **Python**: ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right ``` **JavaScript**: ```javascript class TreeNode { constructor(val = 0, left = null, right = null) { this.val = val; this.left = left; this.right = right; } } ``` --- ## 树的遍历 ### 1. 深度优先搜索(DFS) #### 中序遍历(左 → 根 → 右) **用途**:BST 可得到有序序列 ```python def inorder(root): result = [] def traverse(node): if not node: return traverse(node.left) result.append(node.val) traverse(node.right) traverse(root) return result ``` #### 前序遍历(根 → 左 → 右) **用途**:复制树、前缀表达式 ```python def preorder(root): result = [] def traverse(node): if not node: return result.append(node.val) traverse(node.left) traverse(node.right) traverse(root) return result ``` #### 后序遍历(左 → 右 → 根) **用途**:删除树、后缀表达式 ```python def postorder(root): result = [] def traverse(node): if not node: return traverse(node.left) traverse(node.right) result.append(node.val) traverse(root) return result ``` ### 2. 广度优先搜索(BFS) **用途**:层序遍历、无权重树中的最短路径 ```python from collections import deque def level_order(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 ``` **时间**:O(n),**空间**:O(w),其中 w 为最大宽度 --- ## 二叉搜索树(BST) ### 属性 - 左子树的值 < 节点值 - 右子树的值 > 节点值 - 左右子树也都是 BST - 中序遍历得到有序序列 ### 常见操作 #### 查找 ```python def search_bst(root, val): if not root or root.val == val: return root if val < root.val: return search_bst(root.left, val) return search_bst(root.right, val) ``` **时间**:O(h),其中 h 为高度(平衡时 O(log n),最坏 O(n)) #### 插入 ```python def insert_bst(root, val): if not root: return TreeNode(val) if val < root.val: root.left = insert_bst(root.left, val) else: root.right = insert_bst(root.right, val) return root ``` #### 删除 ```python def delete_bst(root, val): if not root: return None if val < root.val: root.left = delete_bst(root.left, val) elif val > root.val: root.right = delete_bst(root.right, val) else: # 找到待删除节点 # 情况 1:无子节点 if not root.left and not root.right: return None # 情况 2:只有一个子节点 if not root.left: return root.right if not root.right: return root.left # 情况 3:有两个子节点 # 寻找中序后继(右子树中的最小值) min_node = find_min(root.right) root.val = min_node.val root.right = delete_bst(root.right, min_node.val) return root def find_min(node): while node.left: node = node.left return node ``` --- ## 常见树算法 ### 1. 树的高度/深度 ```python def max_depth(root): if not root: return 0 return 1 + max(max_depth(root.left), max_depth(root.right)) ``` ### 2. 平衡树检查 ```python def is_balanced(root): def height(node): if not node: return 0 left_height = height(node.left) if left_height == -1: return -1 right_height = height(node.right) if right_height == -1: return -1 if abs(left_height - right_height) > 1: return -1 return 1 + max(left_height, right_height) return height(root) != -1 ``` ### 3. 最近公共祖先(BST) ```python def lowest_common_ancestor_bst(root, p, q): if p.val < root.val and q.val < root.val: return lowest_common_ancestor_bst(root.left, p, q) if p.val > root.val and q.val > root.val: return lowest_common_ancestor_bst(root.right, p, q) return root ``` ### 4. 二叉树的直径 ```python def diameter_of_binary_tree(root): diameter = 0 def height(node): nonlocal diameter if not node: return 0 left = height(node.left) right = height(node.right) diameter = max(diameter, left + right) return 1 + max(left, right) height(root) return diameter ``` ### 5. 序列化与反序列化 ```python def serialize(root): """将树编码为字符串。""" def helper(node): if not node: return 'null,' return str(node.val) + ',' + helper(node.left) + helper(node.right) return helper(root) def deserialize(data): """将字符串解码为树。""" def helper(nodes): val = next(nodes) if val == 'null': return None node = TreeNode(int(val)) node.left = helper(nodes) node.right = helper(nodes) return node return helper(iter(data.split(','))) ``` --- ## 图 ### 核心概念 **图**是由边连接的节点(顶点)集合。 **类型**: - **有向图**与**无向图**:边是否有方向 - **有权图**与**无权图**:边是否带有权重 - **有环图**与**无环图**:是否包含环 - **连通图**与**非连通图**:所有节点之间是否存在路径 ### 表示方法 #### 1. 邻接表(最常用) ```python # 无向图 graph = { 'A': ['B', 'C'], 'B': ['A', 'D', 'E'], 'C': ['A', 'F'], 'D': ['B'], 'E': ['B', 'F'], 'F': ['C', 'E'] } # 或使用 defaultdict from collections import defaultdict graph = defaultdict(list) graph['A'].append('B') graph['B'].append('A') ``` **空间**:O(V + E) #### 2. 邻接矩阵 ```python # graph[i][j] = 1 表示存在从 i 到 j 的边 n = 5 # 顶点数 graph = [[0] * n for _ in range(n)] graph[0][1] = 1 # 从 0 到 1 的边 graph[1][0] = 1 # 从 1 到 0 的边(无向) ``` **空间**:O(V²) --- ## 图的遍历 ### 1. 深度优先搜索(DFS) **递归**: ```python def dfs(graph, start, visited=None): if visited is None: visited = set() visited.add(start) print(start) for neighbor in graph[start]: if neighbor not in visited: dfs(graph, neighbor, visited) return visited ``` **迭代**(使用栈): ```python def dfs_iterative(graph, start): visited = set() stack = [start] while stack: node = stack.pop() if node not in visited: visited.add(node) print(node) for neighbor in graph[node]: if neighbor not in visited: stack.append(neighbor) return visited ``` **时间**:O(V + E),**空间**:O(V) ### 2. 广度优先搜索(BFS) ```python from collections import deque def bfs(graph, start): visited = set([start]) queue = deque([start]) while queue: node = queue.popleft() print(node) for neighbor in graph[node]: if neighbor not in visited: visited.add(neighbor) queue.append(neighbor) return visited ``` **时间**:O(V + E),**空间**:O(V) --- ## 常见图算法 ### 1. 环检测(无向图) ```python def has_cycle(graph): visited = set() def dfs(node, parent): visited.add(node) for neighbor in graph[node]: if neighbor not in visited: if dfs(neighbor, node): return True elif neighbor != parent: return True # 发现环 return False for node in graph: if node not in visited: if dfs(node, None): return True return False ``` ### 2. 环检测(有向图) ```python def has_cycle_directed(graph): WHITE, GRAY, BLACK = 0, 1, 2 color = {node: WHITE for node in graph} def dfs(node): color[node] = GRAY for neighbor in graph[node]: if color[neighbor] == GRAY: return True # 发现回边 if color[neighbor] == WHITE and dfs(neighbor): return True color[node] = BLACK return False for node in graph: if color[node] == WHITE: if dfs(node): return True return False ``` ### 3. 拓扑排序(DAG) ```python def topological_sort(graph): visited = set() stack = [] def dfs(node): visited.add(node) for neighbor in graph[node]: if neighbor not in visited: dfs(neighbor) stack.append(node) for node in graph: if node not in visited: dfs(node) return stack[::-1] # 反转 ``` **时间**:O(V + E) ### 4. 最短路径(无权图 - BFS) ```python from collections import deque def shortest_path_bfs(graph, start, end): queue = deque([(start, [start])]) visited = set([start]) while queue: node, path = queue.popleft() if node == end: return path for neighbor in graph[node]: if neighbor not in visited: visited.add(neighbor) queue.append((neighbor, path + [neighbor])) return None # 未找到路径 ``` ### 5. Dijkstra 算法(有权图) ```python import heapq def dijkstra(graph, start): """找到从起点到所有节点的最短路径。""" distances = {node: float('inf') for node in graph} distances[start] = 0 pq = [(0, start)] # (距离, 节点) while pq: current_dist, current_node = heapq.heappop(pq) if current_dist > distances[current_node]: continue for neighbor, weight in graph[current_node]: distance = current_dist + weight if distance < distances[neighbor]: distances[neighbor] = distance heapq.heappush(pq, (distance, neighbor)) return distances ``` **时间**:O((V + E) log V)(使用最小堆) ### 6. 并查集(不相交集合) ```python class UnionFind: def __init__(self, n): self.parent = list(range(n)) self.rank = [0] * n def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) # 路径压缩 return self.parent[x] def union(self, x, y): root_x = self.find(x) root_y = self.find(y) if root_x == root_y: return False # 按秩合并 if self.rank[root_x] < self.rank[root_y]: self.parent[root_x] = root_y elif self.rank[root_x] > self.rank[root_y]: self.parent[root_y] = root_x else: self.parent[root_y] = root_x self.rank[root_x] += 1 return True ``` **用途**:环检测、Kruskal 最小生成树、连通分量 --- ## 常见图问题 ### 1. 岛屿数量 ```python def num_islands(grid): if not grid: return 0 count = 0 rows, cols = len(grid), len(grid[0]) def dfs(r, c): if (r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] == '0'): return grid[r][c] = '0' # 标记为已访问 dfs(r + 1, c) dfs(r - 1, c) dfs(r, c + 1) dfs(r, c - 1) for r in range(rows): for c in range(cols): if grid[r][c] == '1': count += 1 dfs(r, c) return count ``` ### 2. 课程表(环检测) ```python def can_finish(num_courses, prerequisites): graph = defaultdict(list) for course, prereq in prerequisites: graph[course].append(prereq) WHITE, GRAY, BLACK = 0, 1, 2 color = [WHITE] * num_courses def has_cycle(course): color[course] = GRAY for prereq in graph[course]: if color[prereq] == GRAY: return True if color[prereq] == WHITE and has_cycle(prereq): return True color[course] = BLACK return False for course in range(num_courses): if color[course] == WHITE: if has_cycle(course): return False return True ``` ### 3. 克隆图 ```python def clone_graph(node): if not node: return None clones = {} def dfs(node): if node in clones: return clones[node] clone = Node(node.val) clones[node] = clone for neighbor in node.neighbors: clone.neighbors.append(dfs(neighbor)) return clone return dfs(node) ``` --- ## 何时使用何种方法 **树的遍历**: - **DFS(中序)**:BST → 有序序列 - **DFS(前序)**:复制树、前缀表示法 - **DFS(后序)**:删除树、后缀表示法 - **BFS**:层序遍历、最短路径 **图的遍历**: - **DFS**:环检测、拓扑排序、连通分量 - **BFS**:最短路径(无权图)、按层探索 **最短路径**: - **BFS**:无权图 - **Dijkstra**:有权图(非负权重) - **Bellman-Ford**:有权图(可含负权重) - **Floyd-Warshall**:所有点对最短路径 **树/图表示选择**: - **邻接表**:稀疏图(E << V²) - **邻接矩阵**:稠密图、快速边查询