Files
2026-07-13 21:36:32 +08:00

684 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 树与图参考
## 二叉树
### 核心概念
**二叉树**是一种层次化数据结构,每个节点最多有两个子节点(左子节点与右子节点)。
**关键属性**
- 每个节点最多有 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²)
- **邻接矩阵**:稠密图、快速边查询