图论基础:从存储到遍历的完整指南

摘要

图(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

总结

图数据结构是处理复杂关系网络的强大工具,从简单的社交网络到复杂的路由系统都有广泛应用。掌握不同的图表示方法和算法,能够帮助解决各种现实世界中的连接和路径问题。

"图是关系的语言,算法是探索关系的工具。"