直击图论核心:高频考点与经典算法题深度解析

图论基础概念:构建知识框架

图论作为计算机科学的核心分支,其数据结构由顶点(Vertex)和边(Edge)构成。根据边的特性,图可分为无向图(Undirected Graph)和有向图(Directed Graph),前者边无方向性(如社交网络中的好友关系),后者边具有方向(如网页链接结构)。加权图(Weighted Graph)通过边权重表示距离或成本,在路径规划中应用广泛。

图的存储方式直接影响算法效率。邻接矩阵使用二维数组存储边信息,时间复杂度为O(1)的边查询,但空间复杂度达O(V²),适合稠密图。邻接表通过链表或数组存储顶点邻接关系,空间复杂度优化至O(V+E),成为稀疏图的首选。例如,LeetCode 733题(图像渲染)可通过邻接表实现区域填充算法。

图的遍历是算法设计的基础。深度优先搜索(DFS)采用递归或栈实现,适用于连通性检测和拓扑排序。广度优先搜索(BFS)通过队列实现,在无权图最短路径求解中表现优异,如LeetCode 133题(克隆图)的节点复制。

核心算法:破解高频考点

最短路径算法:Dijkstra与Floyd-Warshall

Dijkstra算法通过贪心策略解决单源最短路径问题,适用于非负权图。其核心步骤包括:初始化距离数组、维护优先队列、松弛操作更新最短路径。时间复杂度为O((V+E)logV),在导航系统路径规划中广泛应用。例如,LeetCode 743题(网络延迟时间)要求计算从源点到所有节点的最短时间,Dijkstra算法通过优先队列优化可高效解决。

Floyd-Warshall算法采用动态规划思想,解决所有节点对的最短路径问题。通过三重循环更新距离矩阵,时间复杂度为O(V³),适用于负权边(无负权环)场景。在金融套利检测中,该算法可识别货币兑换的最优路径。

最小生成树:Prim与Kruskal

Prim算法从任意顶点出发,逐步扩展生成树,每次选择连接生成树与非生成树顶点的最小权边。使用优先队列优化后,时间复杂度为O(ElogV),适用于稠密图。Kruskal算法则按边权排序,通过并查集(Union-Find)检测环路,时间复杂度为O(ElogE),在稀疏图中表现更优。例如,LeetCode 1584题(连接所有点的最小费用)可通过Kruskal算法求解。

拓扑排序:检测有向无环图

拓扑排序通过DFS或Kahn算法(入度统计)实现,用于任务调度和课程安排。Kahn算法步骤包括:统计入度、初始化队列、处理入度为0的节点、更新邻接节点入度。若最终排序节点数不等于总节点数,则说明图中存在环。LeetCode 207题(课程表)要求判断课程安排是否可行,拓扑排序可高效检测依赖关系。

经典算法题解析:实战提升能力

LeetCode 210题:课程表II

本题要求返回课程安排的拓扑序列。解法步骤如下:

  1. 构建邻接表存储课程依赖关系
  2. 统计每门课程的入度
  3. 初始化队列,将入度为0的课程入队
  4. 依次处理队列中的课程,减少邻接课程的入度
  5. 若邻接课程入度为0,则入队
  6. 最终检查是否所有课程均被处理
  1. from collections import deque
  2. def findOrder(numCourses, prerequisites):
  3. adj = [[] for _ in range(numCourses)]
  4. in_degree = [0] * numCourses
  5. for course, pre in prerequisites:
  6. adj[pre].append(course)
  7. in_degree[course] += 1
  8. queue = deque([i for i in range(numCourses) if in_degree[i] == 0])
  9. result = []
  10. while queue:
  11. node = queue.popleft()
  12. result.append(node)
  13. for neighbor in adj[node]:
  14. in_degree[neighbor] -= 1
  15. if in_degree[neighbor] == 0:
  16. queue.append(neighbor)
  17. return result if len(result) == numCourses else []

LeetCode 787题:K站中转内最便宜的航班

本题要求在限定中转次数内找到最便宜航班。解法结合BFS与动态规划:

  1. 构建邻接表存储航班信息
  2. 初始化距离数组,记录中转k次的最小成本
  3. 逐层扩展,更新距离数组
  4. 最终返回最小成本或-1
  1. import heapq
  2. def findCheapestPrice(n, flights, src, dst, k):
  3. adj = [[] for _ in range(n)]
  4. for u, v, price in flights:
  5. adj[u].append((v, price))
  6. heap = [(0, src, k + 1)]
  7. visited = {}
  8. while heap:
  9. cost, node, stops = heapq.heappop(heap)
  10. if node == dst:
  11. return cost
  12. if stops > 0:
  13. for neighbor, price in adj[node]:
  14. if (neighbor, stops - 1) not in visited or cost + price < visited[(neighbor, stops - 1)]:
  15. visited[(neighbor, stops - 1)] = cost + price
  16. heapq.heappush(heap, (cost + price, neighbor, stops - 1))
  17. return -1

优化策略与注意事项

  1. 算法选择:根据问题特性选择算法。如单源最短路径优先Dijkstra,所有节点对最短路径选择Floyd-Warshall。
  2. 数据结构优化:优先队列可加速Dijkstra算法,并查集可高效实现Kruskal算法。
  3. 边界条件处理:注意负权边、负权环、图不连通等特殊情况。
  4. 空间复杂度控制:邻接表在稀疏图中更节省空间,邻接矩阵适合稠密图。

总结与展望

图论算法在路径规划、网络优化、任务调度等领域具有广泛应用。掌握DFS/BFS遍历、最短路径、最小生成树、拓扑排序等核心算法,结合LeetCode经典题目练习,可显著提升算法设计与实现能力。未来,随着图神经网络(GNN)的发展,图论算法将在社交网络分析、推荐系统等领域发挥更大作用。开发者应持续关注算法优化与工程实现,构建高效、可扩展的图处理系统。