菜单

图算法

相关源文件

本页面全面概述了技术面试中常见的图算法。图算法是算法知识的重要组成部分,尤其适用于解决与网络、连接和遍历相关的问题。本指南涵盖了遍历技术、最短路径算法和最小生成树算法,包括它们的实现和时间复杂度。

有关用于表示图的数据结构的信息,请参阅树和图

图的表示方法

在深入探讨图算法之前,了解图如何在代码中表示非常重要。

表示方式空间复杂度检查边是否存在遍历边最适合
邻接矩阵O(V²)O(1)O(V²)稠密图
邻接表O(V + E)O(degree)O(V + E)稀疏图
边列表O(E)O(E)O(E)需要边操作的算法

来源: README.md:175-186

图遍历算法

图遍历是访问图中所有顶点的过程。两种基本的图遍历算法是深度优先搜索(DFS)和广度优先搜索(BFS)。

深度优先搜索(DFS)

深度优先搜索在回溯之前会沿着每条分支尽可能深地探索。它使用一个栈(通常通过递归实现)来跟踪要探索的顶点。

时间复杂度O(|V| + |E|),其中 V 是顶点数,E 是边数。

应用程序:

  • 检测环
  • 路径查找
  • 拓扑排序
  • 无向图中的连通分量
  • 有向图中的强连通分量

来源: README.md:232-236

广度优先搜索 (BFS)

广度优先搜索在移动到下一深度级别的节点之前,会探索当前深度的所有邻居节点。它使用一个队列来跟踪要访问的顶点。

时间复杂度O(|V| + |E|),其中 V 是顶点数,E 是边数。

应用程序:

  • 无权图中的最短路径
  • 连通分量
  • 网络分析
  • 层序遍历
  • 查找连通分量内的所有节点

来源: README.md:238-242

最短路径算法

最短路径算法寻找图中顶点之间成本(距离、时间等)最小的路径。

Dijkstra 算法

Dijkstra 算法用于查找图中边权重为非负数的节点之间的最短路径。

时间复杂度:使用优先级队列的数组实现时为 O(|V|²)。使用二叉堆实现时为 O((V+E)log V)

应用程序:

  • 导航系统
  • 网络路由协议
  • 航班调度

来源: README.md:250-252

Bellman-Ford 算法

Bellman-Ford 算法计算加权图中从单个源顶点到所有其他顶点的最短路径,即使存在负边权重也能处理。

时间复杂度:

  • 最佳情况:O(|E|)
  • 最差情况:O(|V||E|)

应用程序:

  • 网络(RIP 协议)
  • 带有负边权重的系统
  • 检测负环

来源: README.md:257-262

Floyd-Warshall 算法

Floyd-Warshall 算法在带有正或负边权重(但没有负环)的加权图中查找所有顶点对之间的最短路径。

时间复杂度:所有情况下均为 O(|V|³)

应用程序:

  • 所有顶点对的最短路径
  • 有向图的传递闭包
  • 检测负环
  • 网络路由

来源: README.md:266-273

最小生成树算法

最小生成树(MST)是连通无向图的边的子集,它连接所有顶点,且总边权重尽可能小。

普里姆算法

Prim 算法是一种贪婪算法,用于查找加权无向图的最小生成树。

时间复杂度:使用数组实现时为 O(|V|²)。使用二叉堆时为 O(E log V)

应用程序:

  • 网络设计
  • NP 难问题(如旅行商问题)的近似算法
  • 聚类分析

来源: README.md:275-278

克鲁斯卡尔算法

Kruskal 算法也是一种用于查找最小生成树的贪婪算法,但它的工作原理是排序所有边,然后如果它们不形成环则逐渐添加它们。

时间复杂度:由于排序步骤,为 O(|E|log|V|)

应用程序:

  • 网络设计
  • 近似算法
  • 电路设计

来源: README.md:282-286

其他重要的图算法

拓扑排序

有向图的拓扑排序或拓扑顺序是其顶点的一种线性排序,使得对于每条有向边 (u, v),顶点 u 在排序中都出现在 v 之前。

时间复杂度O(|V| + |E|)

应用程序:

  • 调度带依赖关系的任务
  • 构建系统
  • 课程先决条件
  • 数据序列化

来源: README.md:245-248

面试中常见的图问题

图算法在技术面试中经常被考查。以下是一些常见的问题类型:

  1. 连通性问题

    • 查找无向图中的连通分量
    • 查找有向图中的强连通分量
    • 判断两个节点是否连通
  2. 路径查找问题

    • 查找两个节点之间的最短路径(无权图用 BFS,加权图用 Dijkstra/Bellman-Ford)
    • 查找两个节点之间的所有路径
    • 判断两个节点之间是否存在路径
  3. 环检测

    • 检测图是否包含环
    • 查找图中的所有环
    • 检测负权重环
  4. 图着色和二分图检查

    • 检查图是否是二分图
    • 用最少颜色对图进行着色
  5. 网络流问题

    • 最大流
    • 最小割
  6. 特殊图问题

    • 克隆图
    • 查找桥和关节点
    • 单词梯/转换问题

来源: README.md:230-286

实现考量

在面试中实现图算法时,请考虑以下几点:

  1. 图表示方式的选择:

    • 邻接表通常更适用于稀疏图(大多数实际图)
    • 邻接矩阵对于稠密图或需要快速查找边时可能更简单
  2. 访问跟踪:

    • 始终有机制跟踪已访问节点以避免循环
    • 可以使用布尔数组、HashSet 或位操作
  3. 队列/栈实现:

    • 对于 BFS,使用队列
    • 对于 DFS,使用递归或显式栈
  4. 边界情况:

    • 空图
    • 单节点图
    • 不连通图
    • 带环的图
  5. 性能优化:

    • Dijkstra 算法的优先级队列
    • Kruskal 算法的并查集数据结构

来源: README.md:175-286

参考实现示例

该存储库包含多个图问题的实现示例。虽然不涵盖所有图算法,但这些示例展示了如何应用图概念来解决问题。

BFS 实现

来自 BreadthFirstSearch 文件夹

  • cloneGraph.java: 演示如何使用 BFS 克隆图
  • pacificAtlanticWaterFlow.java: 使用 BFS 解决基于网格的水流问题

DFS 实现

来自 DepthFirstSearch 文件夹

  • numberOfIslands.java: 使用 DFS 计算网格中的连通分量
  • sameTree.java: 使用 DFS 比较树结构

来源: README.md:416-431

时间与空间复杂度总结

算法时间复杂度空间复杂度
深度优先搜索(DFS)O(V + E)O(V)
广度优先搜索 (BFS)O(V + E)O(V)
拓扑排序O(V + E)O(V)
Dijkstra 算法O(V²) 或 O((V+E)log V)O(V)
Bellman-Ford 算法O(V·E)O(V)
Floyd-Warshall 算法O(V³)O(V²)
普里姆算法O(V²) 或 O(E log V)O(V)
克鲁斯卡尔算法O(E log V)O(V)

来源: README.md:232-286