图论基础:从存储到遍历的完整指南
摘要
图(Graph)是表示多对多关系的非线性数据结构,广泛应用于社交网络、路径规划、推荐系统等领域。本文将全面介绍图的存储表示、遍历算法、最短路径问题以及实际应用场景。
1. 图的基本概念与术语
1.1 图的定义与分类
图G由顶点集合V和边集合E组成,记为G=(V,E)
class Graph:
def __init__(self):
self.vertices = {} # 顶点字典
self.edges = [] # 边列表
图的分类:
| 类型 | 特点 | 示例 |
|---|---|---|
| 无向图 | 边没有方向 | 社交网络 |
| 有向图 | 边有方向 | 网页链接 |
| 加权图 | 边有权重 | 道路网络 |
| 无权图 | 边无权重 | 合作关系图 |
1.2 重要术语
- 顶点(Vertex):图的基本元素
- 边(Edge):顶点间的连接
- 度(Degree):顶点连接的边数
- 路径(Path):顶点序列
- 连通图:任意两顶点间都有路径
- 完全图:每对顶点间都有边
2. 图的存储表示
2.1 邻接矩阵(Adjacency Matrix)
class AdjacencyMatrixGraph:
def __init__(self, num_vertices):
self.num_vertices = num_vertices
self.matrix = [[0] * num_vertices for _ in range(num_vertices)]
def add_edge(self, u, v, weight=1):
self.matrix[u][v] = weight
# 如果是无向图,还需要设置对称位置
self.matrix[v][u] = weight
def get_neighbors(self, v):
neighbors = []
for i in range(self.num_vertices):
if self.matrix[v][i] != 0:
neighbors.append((i, self.matrix[v][i]))
return neighbors
2.2 邻接表(Adjacency List)
from collections import defaultdict
class AdjacencyListGraph:
def __init__(self):
self.graph = defaultdict(list)
def add_edge(self, u, v, weight=1):
self.graph[u].append((v, weight))
# 无向图需要添加反向边
self.graph[v].append((u, weight))
def get_neighbors(self, v):
return self.graph[v]
2.3 存储方式对比
| 特性 | 邻接矩阵 | 邻接表 |
|---|---|---|
| 空间复杂度 | O(V²) | O(V+E) |
| 检查边存在 | O(1) | O(degree(V)) |
| 获取所有边 | O(V) | O(degree(V)) |
| 添加顶点 | O(V²) | O(1) |
| 添加边 | O(1) | O(1) |
| 适用场景 | 稠密图 | 稀疏图 |
3. 图的遍历算法
3.1 广度优先搜索(BFS)
from collections import deque
def bfs(graph, start):
"""邻接表表示的BFS"""
visited = set()
queue = deque([start])
visited.add(start)
result = []
while queue:
vertex = queue.popleft()
result.append(vertex)
for neighbor, _ in graph.get_neighbors(vertex):
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
return result
3.2 深度优先搜索(DFS)
def dfs_recursive(graph, vertex, visited=None, result=None):
"""递归DFS"""
if visited is None:
visited = set()
if result is None:
result = []
visited.add(vertex)
result.append(vertex)
for neighbor, _ in graph.get_neighbors(vertex):
if neighbor not in visited:
dfs_recursive(graph, neighbor, visited, result)
return result
def dfs_iterative(graph, start):
"""迭代DFS"""
visited = set()
stack = [start]
result = []
while stack:
vertex = stack.pop()
if vertex not in visited:
visited.add(vertex)
result.append(vertex)
# 逆序添加邻居以保证顺序一致性
for neighbor, _ in reversed(graph.get_neighbors(vertex)):
if neighbor not in visited:
stack.append(neighbor)
return result
4. 最短路径算法
4.1 Dijkstra算法(单源最短路径)
import heapq
def dijkstra(graph, start):
"""Dijkstra算法实现"""
distances = {vertex: float('infinity') for vertex in graph.graph}
distances[start] = 0
pq = [(0, start)]
while pq:
current_distance, current_vertex = heapq.heappop(pq)
if current_distance > distances[current_vertex]:
continue
for neighbor, weight in graph.get_neighbors(current_vertex):
distance = current_distance + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(pq, (distance, neighbor))
return distances
4.2 Floyd-Warshall算法(多源最短路径)
def floyd_warshall(adj_matrix):
"""Floyd-Warshall算法实现"""
n = len(adj_matrix)
dist = [[float('infinity')] * n for _ in range(n)]
# 初始化距离矩阵
for i in range(n):
for j in range(n):
if i == j:
dist[i][j] = 0
elif adj_matrix[i][j] != 0:
dist[i][j] = adj_matrix[i][j]
# 动态规划更新
for k in range(n):
for i in range(n):
for j in range(n):
if dist[i][j] > dist[i][k] + dist[k][j]:
dist[i][j] = dist[i][k] + dist[k][j]
return dist
5. 最小生成树算法
5.1 Prim算法
def prim(graph):
"""Prim算法实现最小生成树"""
if not graph.graph:
return []
mst = []
visited = set()
start_vertex = next(iter(graph.graph))
visited.add(start_vertex)
edges = [
(weight, start_vertex, neighbor)
for neighbor, weight in graph.get_neighbors(start_vertex)
]
heapq.heapify(edges)
while edges and len(visited) < len(graph.graph):
weight, u, v = heapq.heappop(edges)
if v not in visited:
visited.add(v)
mst.append((u, v, weight))
for neighbor, w in graph.get_neighbors(v):
if neighbor not in visited:
heapq.heappush(edges, (w, v, neighbor))
return mst
5.2 Kruskal算法
class UnionFind:
"""并查集用于Kruskal算法"""
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
else:
self.parent[root_y] = root_x
if self.rank[root_x] == self.rank[root_y]:
self.rank[root_x] += 1
return True
def kruskal(graph, num_vertices):
"""Kruskal算法实现"""
edges = []
for u in graph.graph:
for v, weight in graph.get_neighbors(u):
edges.append((weight, u, v))
edges.sort()
uf = UnionFind(num_vertices)
mst = []
for weight, u, v in edges:
if uf.union(u, v):
mst.append((u, v, weight))
return mst
6. 实际应用场景
6.1 社交网络分析
class SocialNetworkGraph:
def __init__(self):
self.graph = AdjacencyListGraph()
def add_friendship(self, user1, user2):
self.graph.add_edge(user1, user2)
def find_mutual_friends(self, user1, user2):
"""查找共同好友"""
friends1 = set(neighbor for neighbor, _ in self.graph.get_neighbors(user1))
friends2 = set(neighbor for neighbor, _ in self.graph.get_neighbors(user2))
return friends1 & friends2
def suggest_friends(self, user, degree=2):
"""好友推荐(二度人脉)"""
visited = set([user])
suggestions = set()
# BFS遍历二度人脉
queue = deque([(user, 0)])
while queue:
current, dist = queue.popleft()
if dist == degree:
continue
for neighbor, _ in self.graph.get_neighbors(current):
if neighbor not in visited:
visited.add(neighbor)
if dist == degree - 1: # 二度人脉
suggestions.add(neighbor)
queue.append((neighbor, dist + 1))
return suggestions
6.2 路径规划系统
class NavigationSystem:
def __init__(self):
self.graph = AdjacencyListGraph()
def add_road(self, location1, location2, distance, traffic_factor=1.0):
weight = distance * traffic_factor
self.graph.add_edge(location1, location2, weight)
def find_shortest_path(self, start, end):
"""A*算法优化路径查找"""
# 简化的Dijkstra实现
return dijkstra(self.graph, start)[end]
def find_all_paths(self, start, end, max_stops=10):
"""查找所有可能路径"""
def dfs_paths(current, path, stops):
if stops > max_stops:
return
if current == end:
all_paths.append(path.copy())
return
for neighbor, weight in self.graph.get_neighbors(current):
if neighbor not in path:
path.append(neighbor)
dfs_paths(neighbor, path, stops + 1)
path.pop()
all_paths = []
dfs_paths(start, [start], 0)
return all_paths
7. 性能优化与高级话题
7.1 图算法复杂度对比
| 算法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| BFS | O(V+E) | O(V) | 无权图最短路径 |
| DFS | O(V+E) | O(V) | 拓扑排序、连通分量 |
| Dijkstra | O(E log V) | O(V) | 带权图单源最短路径 |
| Floyd-Warshall | O(V³) | O(V²) | 所有顶点对最短路径 |
| Prim | O(E log V) | O(V) | 最小生成树 |
| Kruskal | O(E log V) | O(V) | 最小生成树 |
7.2 内存优化技巧
class CompressedGraph:
"""使用压缩存储优化大型图"""
def __init__(self):
self.vertex_map = {} # 顶点到索引的映射
self.adjacency_list = [] # 压缩的邻接表
self.offsets = [] # 偏移量数组
def add_vertex(self, vertex):
if vertex not in self.vertex_map:
self.vertex_map[vertex] = len(self.offsets)
self.offsets.append(len(self.adjacency_list))
def add_edge(self, u, v, weight):
u_idx = self.vertex_map[u]
v_idx = self.vertex_map[v]
# 添加到邻接表
self.adjacency_list.append((v_idx, weight))
# 更新偏移量
for i in range(u_idx + 1, len(self.offsets)):
self.offsets[i] += 1
总结
图数据结构是处理复杂关系网络的强大工具,从简单的社交网络到复杂的路由系统都有广泛应用。掌握不同的图表示方法和算法,能够帮助解决各种现实世界中的连接和路径问题。
"图是关系的语言,算法是探索关系的工具。"
















