寻找图中是否存在路径

简介: 本节介绍了如何使用并查集判断图中两个顶点之间是否存在有效路径。通过将图的边信息存储到并查集中,同一连通区域的顶点会处于同一集合。只需比较两个顶点的根节点是否相同,即可判断它们是否连通。文中提供了C语言、Java和Python的实现代码,并通过示例验证了算法的正确性。

我们在《并查集》一节详细介绍了并查集这种存储结构,本节趁热打铁,带领读者用并查集解决一个实际问题,即判断图中指定的两个顶点之间是否存在有效的路径。


举个简单的例子,观察下图:


图 1 包含 6 个顶点的图


这是一张包含 6 个顶点的图,其中顶点 1 和顶点 2 是连通的,它们之间存在的有效路径是1-0-2;而顶点 1 和 顶点 5 之间是不连通的,它们之间不存在有效的路径。  

并查集判断图中是否存在路径

判断图中指定的两个顶点之间是否存在有效路径,用并查集解决此问题的思路是:将整张图存储到并查集中,图中相互连接的各个顶点同处一个集合,它们的根结点是相同的。也就是说,对于图中指定的两个顶点,如果他们的根结点相同,就表明它们是连通的,它们之间就一定存在有效的路径;反之如果它们的根结点不同,表明它们处于不同的集合,它们不是连通的,它们之间就不存在有效的路径。


仍以图 1 为例,把整张图(n = 6, edges = [ [0,1], [0,2], [3,5], [5,4], [4,3] ])存储到并查集中,最终并查集的存储状态如下图所示:


图 2 存储图的并查集


可以看到,下标 0 、1 和 2 位置上存储的根结点都是 0,表明顶点 0、1 和 2 同处一个集合,它们之间是相互连通的;同理,下标 3、4 和 5 位置上存储的根结点都是 3,表明顶点 3、4 和 5 同处一个集合,它们之间是相互连通的。


判断图中两个顶点之间是否存在有效的路径,只需要分别找到两个顶点的根结点,如果它们的根结点相同,表明它们同处一个集合,它们之间就至少存在一条有效的路径;反之如果根结点不同,表明它们身处不同的集合,它们之间不存在有效的路径。

并查集解决图中是否存在路径

下面是用并查集解决“图中是否存在路径”的C语言程序:

  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. typedef enum { false, true } bool;
  4. #define MAX_SIZE 1000  // 可以根据需要调整最大尺寸

  5. // 并查集数组
  6. typedef struct {
  7. int parent[MAX_SIZE];
  8. int count;
  9. }UnionFind;

  10. // 初始化并查集
  11. void initialize(UnionFind* uf, int size) {
  12. for (int i = 0; i < size; i++) {
  13.        uf->parent[i] = i;
  14. }
  15.    uf->count = size;
  16. }

  17. // 查找根节点(代表元素)
  18. int find(UnionFind* uf, int x) {
  19. if (uf->parent[x] != x) {
  20.        uf->parent[x] = find(uf, uf->parent[x]); // 路径压缩
  21. }
  22. return uf->parent[x];
  23. }

  24. // 合并两个元素所在的集合
  25. void unionSets(UnionFind* uf, int x, int y) {
  26. //找到两个元素所在集合的代表元素
  27. int xRoot = find(uf, x);
  28. int yRoot = find(uf, y);
  29. //如果代表元素不同,表明它们是两个集合,将它们合并
  30. if (xRoot != yRoot) {
  31.        uf->parent[xRoot] = yRoot; // uf->parent[yRoot] = xRoot;
  32.        uf->count--;
  33. }
  34. }

  35. //判断图中是否存在路径,n 表示图中顶点的数量,edgs数组存储的是图中所有的边,len 表示边的数量,source 和 destination 表示要进行判断的两个顶点
  36. bool validPath(int n, int(*edgs)[2], int len, int source, int destination) {
  37. UnionFind uf;
  38. initialize(&uf, n);
  39. // 把图存储到并查集中
  40. for (int  i = 0; i < len; i++)
  41. {
  42. unionSets(&uf, edgs[i][0], edgs[i][1]);
  43. }
  44. //判断 source 和 destination 是否同处一个集合
  45. if (find(&uf, source) == find(&uf, destination)) {
  46. return true;
  47. }
  48. else
  49. {
  50. return false;
  51. }
  52. }

  53. // 主函数
  54. int main() {
  55. int edgs[5][2] = { {0,1},{0,2},{3,5},{5,4},{4,3} };

  56. //判断顶点 1 和 2 之间是否存在有效的路径
  57. bool ret = validPath(6, edgs, 5, 1, 2);
  58. if (ret == true) {
  59. printf("顶点 1 和顶点 2 之间存在有效的路径\n");
  60. }
  61. else
  62. {
  63. printf("顶点 1 和顶点 2 不存在有效的路径\n");
  64. }

  65. //判断顶点 1 和 5 之间是否存在有效的路径
  66.    ret = validPath(6, edgs, 5, 1, 5);
  67. if (ret == true) {
  68. printf("顶点 1 和顶点 5 之间存在有效的路径\n");
  69. }
  70. else
  71. {
  72. printf("顶点 1 和顶点 5 不存在有效的路径\n");
  73. }

  74. return 0;
  75. }


下面是用并查集解决“图中是否存在路径”的 Java 程序:

  1. // 并查集类,用于处理集合合并和查找操作
  2. public class UnionFind {
  3. private int[] parent;  // 记录节点的父节点
  4. private int count;     // 记录集合的数量

  5. // 构造函数,初始化并查集,每个节点的父节点初始为自己,集合数量为节点个数
  6. public UnionFind(int size) {
  7.        parent = new int[size];
  8. for (int i = 0; i < size; i++) {
  9.            parent[i] = i;
  10. }
  11.        count = size;
  12. }

  13. // 查找根节点(代表元素)的方法,同时进行路径压缩
  14. public int find(int x) {
  15. if (parent[x] != x) {
  16.            parent[x] = find(parent[x]);  // 路径压缩
  17. }
  18. return parent[x];
  19. }

  20. // 合并两个元素所在的集合
  21. public void unionSets(int x, int y) {
  22. int xRoot = find(x);
  23. int yRoot = find(y);
  24. if (xRoot != yRoot) {
  25.            parent[xRoot] = yRoot;  // 合并集合
  26.            count--;
  27. }
  28. }

  29. // 获取当前集合的数量
  30. public int getCount() {
  31. return count;
  32. }
  33. }

  34. // 主程序类,用于测试并查集是否存在有效路径
  35. public class ValidPath {
  36. // 判断图中是否存在路径的方法
  37. public static boolean validPath(int n, int[][] edges, int len, int source, int destination) {
  38. UnionFind uf = new UnionFind(n);
  39. // 将图存储到并查集中
  40. for (int i = 0; i < len; i++) {
  41.            uf.unionSets(edges[i][0], edges[i][1]);
  42. }
  43. // 判断 source 和 destination 是否属于同一个集合
  44. return uf.find(source) == uf.find(destination);
  45. }

  46. // 主函数,用于测试并查集是否能够判断有效路径
  47. public static void main(String[] args) {
  48. int[][] edges = {{0, 1}, {0, 2}, {3, 5}, {5, 4}, {4, 3}};

  49. // 判断顶点 1 和 2 之间是否存在有效的路径
  50. boolean ret = validPath(6, edges, 5, 1, 2);
  51. if (ret) {
  52.            System.out.println("顶点 1 和顶点 2 之间存在有效的路径");
  53. } else {
  54.            System.out.println("顶点 1 和顶点 2 不存在有效的路径");
  55. }

  56. // 判断顶点 1 和 5 之间是否存在有效的路径
  57.        ret = validPath(6, edges, 5, 1, 5);
  58. if (ret) {
  59.            System.out.println("顶点 1 和顶点 5 之间存在有效的路径");
  60. } else {
  61.            System.out.println("顶点 1 和顶点 5 不存在有效的路径");
  62. }
  63. }
  64. }


下面是用并查集解决“图中是否存在路径”的 Python 程序:

  1. class UnionFind:
  2. def __init__(self, size):
  3. # 初始化并查集,每个节点的父节点初始为自己,集合数量为节点个数
  4.        self.parent = [i for i in range(size)]
  5.        self.count = size

  6. def find(self, x):
  7. # 查找根节点(代表元素)的方法,同时进行路径压缩
  8. if self.parent[x] != x:
  9.            self.parent[x] = self.find(self.parent[x])  # 路径压缩
  10. return self.parent[x]

  11. def union_sets(self, x, y):
  12. # 合并两个元素所在的集合
  13.        x_root = self.find(x)
  14.        y_root = self.find(y)
  15. if x_root != y_root:
  16.            self.parent[x_root] = y_root  # 合并集合
  17.            self.count -= 1

  18. def get_count(self):
  19. # 获取当前集合的数量
  20. return self.count

  21. def valid_path(n, edges, source, destination):
  22.    uf = UnionFind(n)
  23. # 将图存储到并查集中
  24. for edge in edges:
  25.        uf.union_sets(edge[0], edge[1])
  26. # 判断 source 和 destination 是否属于同一个集合
  27. return uf.find(source) == uf.find(destination)

  28. if __name__ == "__main__":
  29.    edges = [[0, 1], [0, 2], [3, 5], [5, 4], [4, 3]]

  30. # 判断顶点 1 和 2 之间是否存在有效的路径
  31.    ret = valid_path(6, edges, 1, 2)
  32. if ret:
  33. print("顶点 1 和顶点 2 之间存在有效的路径")
  34. else:
  35. print("顶点 1 和顶点 2 不存在有效的路径")

  36. # 判断顶点 1 和 5 之间是否存在有效的路径
  37.    ret = valid_path(6, edges, 1, 5)
  38. if ret:
  39. print("顶点 1 和顶点 5 之间存在有效的路径")
  40. else:
  41. print("顶点 1 和顶点 5 不存在有效的路径")


运行程序,输出结果为:

顶点 1 和顶点 2 之间存在有效的路径

顶点 1 和顶点 5 不存在有效的路径

相关文章
|
Shell 网络安全 开发工具
Tabby终端工具的配置和使用
Tabby终端工具的配置和使用
10371 0
|
9月前
|
前端开发 安全 Go
GoWind Admin|风行 — 开箱即用的企业级全栈中后台框架・内置微服务接口数据聚合能力
GoWind Admin(风行)是一款开箱即用的企业级全栈中后台框架,专为微服务场景设计。内置高性能、类型安全的数据聚合引擎,支持并发拉取、智能回填、树形结构与DataLoader模式,一键解决N+1查询与跨服务数据拼装难题,大幅提升开发效率与系统性能。
558 2
|
11月前
|
人工智能 前端开发 算法
DeepCode:把论文和想法变成代码的 AI 工具
DeepCode 是香港大学开源的 AI 编码工具,通过多智能体协作实现论文转代码、需求转网站、描述转后端三大功能。采用 MIT 协议,已获 7900+ 星标。适合科研人员、独立开发者和技术学习者使用,能有效提升开发效率。
|
数据采集 监控 Go
用 Go 实现一个轻量级并发任务调度器(支持限速)
本文介绍了如何用 Go 实现一个轻量级的并发任务调度器,解决日常开发中批量任务处理的需求。调度器支持最大并发数控制、速率限制、失败重试及结果收集等功能。通过示例代码展示了其使用方法,并分析了核心组件设计,包括任务(Task)和调度器(Scheduler)。该工具适用于网络爬虫、批量请求等场景。文章最后总结了 Go 并发模型的优势,并提出了扩展功能的方向,如失败回调、超时控制等,欢迎读者交流改进。
659 25
|
机器学习/深度学习 人工智能 算法
AI鱼类识别技术原理及示例代码
本文详细解析了AI鱼类识别的代码示例,涵盖深度学习框架选择、数据集处理、模型构建与训练优化全流程。内容包括技术选型对比(如TensorFlow、PyTorch、YOLO系列)、数据准备流程(开源数据集与标注规范)、完整代码示例(以PyTorch版ResNet50改进模型为例)以及模型优化策略(如量化压缩、知识蒸馏)。此外,还提供了典型应用场景(如渔业资源监测系统)、模型评估指标及开源项目推荐,并针对常见问题(小样本、水下模糊、类别不平衡等)提出解决方案。
1299 5
|
编解码 计算机视觉
RT-DETR改进策略【Head】| 增加针对 大目标 的检测层 (四个检测头)
RT-DETR改进策略【Head】| 增加针对 大目标 的检测层 (四个检测头)
1037 16
|
机器学习/深度学习 人工智能 持续交付
利用AI进行代码审查:提升软件质量的新策略
【10月更文挑战第28天】本文探讨了AI在代码审查中的应用,介绍了AI如何通过静态代码分析、代码风格检查和实时反馈提升代码质量。文章还讨论了将AI工具集成到CI/CD流程、定制化规则和结合人工审查等进阶技巧,并推荐了SonarQube和DeepCode等实用工具。未来,AI代码审查工具将更加智能,助力软件开发。
|
存储 小程序 C语言
【C语言程序设计——文件】文件操作(头歌实践教学平台习题)【合集】
本文介绍了C语言中的文件操作,分为两个关卡。第1关任务是将键盘输入的字符(以#结束)存入`file1.txt`并显示输出;第2关任务是从键盘输入若干行文本(每行不超过80个字符,用-1作为结束标志),写入`file2.txt`后再读取并显示。文中详细讲解了文件的打开、读取(使用`fgetc()`和`fgets()`)、写入(使用`fputc()`和`fputs()`)及关闭操作,并提供了示例代码和测试说明。
674 5
|
SQL 数据库连接 Shell
python连接SqlServer数据库
要使用Python连接SQL Server数据库,你需要先安装pyodbc库,然后使用它来建立连接。
874 1
python连接SqlServer数据库
|
自动驾驶 安全 物联网
探索未来网络:从5G到6G的演进与创新
本文旨在探讨移动通信技术从5G向6G演进的过程及其关键技术,揭示这一领域的最新趋势和挑战。通过分析5G的现状、6G的预期目标和技术特点,本文展示了未来通信技术的广阔前景和潜在应用领域。