引言

图是现实世界中最普适的数据结构之一。从社交网络中的好友关系、城市之间的道路连接,到神经网络中的神经元连接、编译器中的依赖关系,图无处不在。理解并掌握图算法,不仅是计算机科学的基础素养,更是解决复杂工程问题的利器。本文将深入探讨从经典最短路径算法到现代网络流理论的完整图算法体系,并结合实际工程案例展示其应用价值。

一、图的表示与基础

1.1 图的基本定义

图 G = (V, E) 由顶点集 V 和边集 E 组成。根据边是否有向,图可分为有向图(directed graph)和无向图(undirected graph)。边还可携带权重,形成带权图(weighted graph),权重可以表示距离、成本、容量等语义。

1.2 存储方式

两种主流存储方式各有千秋:

  • 邻接矩阵:使用 |V| × |V| 的二维数组,查询边是否存在的时间为 O(1),但空间复杂度为 O(V²),适合稠密图。
  • 邻接表:为每个顶点维护一个邻居列表,空间复杂度为 O(V + E),遍历邻居高效,适合稀疏图,是工程实践中更常用的方式。

二、最短路径算法

2.1 Dijkstra 算法

Dijkstra 算法求解单源最短路径问题(边权非负)。核心思想是贪心策略:每次选择当前距离源点最近的未确定顶点,然后松弛其所有出边。使用二叉堆优化的时间复杂度为 O((V + E) log V)。

def dijkstra(graph, source):
    dist = {v: float('inf') for v in graph}
    dist[source] = 0
    pq = [(0, source)]
    while pq:
        d, u = heappop(pq)
        if d > dist[u]: continue
        for v, w in graph[u]:
            if dist[u] + w < dist[v]:
                dist[v] = dist[u] + w
                heappush(pq, (dist[v], v))
    return dist

工程实践要点:在地图导航中,道路网络通常是稀疏图,Dijkstra 及其变种(如双向 Dijkstra、A* 启发式搜索)是路由引擎的基础。Google Maps 的路线规划就使用了基于 Contraction Hierarchies 的加速技术,将预处理与查询分离。

2.2 Bellman-Ford 算法

当图中存在负权边时,Dijkstra 失效。Bellman-Ford 通过 V-1 轮全局松弛操作,能够处理负权边并检测负权环。时间复杂度为 O(VE)。

应用场景:套利检测是 Bellman-Ford 的经典用例。将货币兑换建模为图,汇率取对数后取负作为边权,负权环即代表套利机会。

2.3 SPFA 算法

Shortest Path Faster Algorithm 是 Bellman-Ford 的队列优化版本,在随机图上平均时间复杂度接近 O(E),但在精心构造的网格图上会退化到 O(VE)。竞赛中常用于判断负权环。

三、全源最短路径

3.1 Floyd-Warshall 算法

基于动态规划的三重循环优雅解法,时间复杂度 O(V³),适合 V ≤ 500 的稠密图。核心转移方程:dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])。

工程扩展:Floyd-Warshall 可以改造为传递闭包(计算可达性),也可以在中点枚举中引入路径记录,还原具体最短路径。

四、图遍历与连通性

4.1 深度优先搜索 (DFS)

DFS 是图算法的基石,时间复杂度 O(V + E)。核心应用包括:

  • 拓扑排序:仅适用于有向无环图(DAG),通过后序遍历的逆序得到
  • 强连通分量:Kosaraju 和 Tarjan 算法是求 SCC 的经典方法
  • 环检测:DFS 中的回边表明有向图存在环
  • 关节点与桥:通过 low-link 值判断图的脆弱点

4.2 广度优先搜索 (BFS)

BFS 按层遍历,天然适合求解无权图的最短路径。在社交网络中,BFS 可以找到两人之间的最短关系链(六度分隔理论的实际验证)。

五、最小生成树

5.1 Kruskal 算法

贪心策略:按边权升序排序,用并查集检测是否形成环。时间复杂度 O(E log E)。适合稀疏图。

5.2 Prim 算法

类似 Dijkstra 的思想,从任意顶点开始逐步扩展树。使用优先队列优化的时间复杂度为 O(E log V)。适合稠密图。

应用场景:网络布线优化、电路板引脚连接、聚类分析中的单链接方法。

六、网络流理论

6.1 最大流问题

给定有向图 G,每条边有容量 c(e),源点 s 和汇点 t,求从 s 到 t 的最大流量。Ford-Fulkerson 方法通过不断寻找增广路径来增加流量。

6.2 Edmonds-Karp 算法

使用 BFS 寻找最短增广路径的 Ford-Fulkerson 实现,时间复杂度 O(VE²),保证了多项式时间复杂度。

6.3 Dinic 算法

引入层次图和阻塞流的概念,时间复杂度 O(V²E)。在单位容量图中可达 O(min(V^(2/3), E^(1/2)) × E),是竞赛和工程中最常用的最大流算法。

class Dinic:
    def __init__(self, n):
        self.n = n
        self.graph = [[] for _ in range(n)]
    
    def add_edge(self, fr, to, cap):
        forward = [to, cap, None]
        backward = [fr, 0, forward]
        forward[2] = backward
        self.graph[fr].append(forward)
        self.graph[to].append(backward)
    
    def bfs_level(self, s, t):
        self.level = [-1] * self.n
        self.level[s] = 0
        queue = deque([s])
        while queue:
            u = queue.popleft()
            for e in self.graph[u]:
                if e[1] > 0 and self.level[e[0]] < 0:
                    self.level[e[0]] = self.level[u] + 1
                    queue.append(e[0])
        return self.level[t] >= 0
    
    def dfs_flow(self, u, t, f):
        if u == t: return f
        for i in range(self.it[u], len(self.graph[u])):
            self.it[u] = i
            e = self.graph[u][i]
            if e[1] > 0 and self.level[u] < self.level[e[0]]:
                d = self.dfs_flow(e[0], t, min(f, e[1]))
                if d > 0:
                    e[1] -= d
                    e[2][1] += d
                    return d
        return 0
    
    def max_flow(self, s, t):
        flow = 0
        INF = float('inf')
        while self.bfs_level(s, t):
            self.it = [0] * self.n
            while True:
                f = self.dfs_flow(s, t, INF)
                if f == 0: break
                flow += f
        return flow

6.4 最大流的最小割定理

最大流最小割定理:网络中从 s 到 t 的最大流量等于分离 s 和 t 的最小割的容量。这一定理将流量优化问题转化为割的优化问题,是图论最优美的对偶性定理之一。

6.5 费用流

在网络流的基础上,每条边增加单位流量的费用,目标是在输送指定流量时最小化总费用(或最大化收益)。连续最短增广路算法(SSAP)通过 SPFA 或势函数+Dijkstra 寻找最短费用增广路。

应用场景:物流调度和分配问题。例如二分图最大权匹配可转化为最小费用最大流问题。

七、二分图与匹配

7.1 二分图最大匹配

匈牙利算法可以在 O(VE) 时间内找到二分图的最大匹配。Hopcroft-Karp 算法进一步优化到 O(E√V)。

7.2 二分图最大权匹配(KM算法)

Kuhn-Munkres 算法通过顶标和相等子图的概念,在 O(V³) 时间内求解最大权完美匹配。

应用场景:任务分配、在线广告中的广告主-展示位置匹配、滴滴/Uber 中的司机-乘客匹配。

八、高级主题与工程前沿

8.1 启发式搜索 A*

A* 算法在 Dijkstra 的基础上加入启发函数 h(n),估计当前节点到目标节点的代价。当 h(n) 满足可纳性(admissible)和一致性(consistent)时,A* 能找到最优解,且显著减少搜索空间。

8.2 图嵌入与表示学习

DeepWalk、Node2Vec 等算法通过随机游走 + Word2Vec 的方式将图节点映射到低维向量空间,保留了图的结构语义。在推荐系统中,用户-物品二分图的嵌入表示是协同过滤的有力补充。

8.3 图神经网络 (GNN)

图神经网络直接在图结构上进行消息传递和特征聚合。GCN、GraphSAGE、GAT 等模型在分子性质预测、社交网络分析、知识图谱补全等领域取得了突破性成果。

8.4 动态图算法

现实世界的图随时间演化。动态连通性动态最短路径等问题要求在边插入/删除后高效更新结果。Euler Tour Tree、Link-Cut Tree 等数据结构提供了理论优雅的工程实现。

九、工程实践总结

问题类型推荐算法时间复杂度适用场景
单源最短路径(非负权)Dijkstra + 堆优化O((V+E)log V)地图导航、网络路由
单源最短路径(含负权)SPFA / Bellman-FordO(VE)套利检测、差分约束
全源最短路径Floyd-WarshallO(V³)小规模稠密图、传递闭包
最小生成树Kruskal / PrimO(E log E)网络设计、聚类
最大流DinicO(V²E)二分图匹配、图像分割
费用流MCMF (SSAP)O(VEF)物流调度、资源分配
二分图匹配Hopcroft-Karp / KMO(E√V) / O(V³)任务分配、推荐系统

十、结语

图算法是连接数学理论与工程实践的桥梁。从 Dijkstra 的贪心智慧到网络流的对偶美学,从 A* 的启发式直觉到 GNN 的深度表征,每一个算法背后都蕴含着深刻的计算思维。在当今日益复杂的大规模图数据处理场景下——万亿边规模的社交图谱、实时动态的交通网络——经典算法与分布式计算框架(如 Pregel、GraphLab)的结合,正在开启图计算的新纪元。

掌握这些核心算法,不仅是面试的需要,更是构建下一代大规模分布式系统的基础能力。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部