寻找图中是否存在路径(非常详细,图文并茂)
我们在《并查集》一节详细介绍了并查集这种存储结构,本节趁热打铁,带领读者用并查集解决一个实际问题,即判断图中指定的两个顶点之间是否存在有效的路径。
举个简单的例子,观察下图:
图 1 包含 6 个顶点的图
这是一张包含 6 个顶点的图,其中顶点 1 和顶点 2 是连通的,它们之间存在的有效路径是
仍以图 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 同处一个集合,它们之间是相互连通的。
判断图中两个顶点之间是否存在有效的路径,只需要分别找到两个顶点的根结点,如果它们的根结点相同,表明它们同处一个集合,它们之间就至少存在一条有效的路径;反之如果根结点不同,表明它们身处不同的集合,它们之间不存在有效的路径。
下面是用并查集解决“图中是否存在路径”的 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语言程序:#include <stdio.h> #include <stdlib.h> typedef enum { false, true } bool; #define MAX_SIZE 1000 // 可以根据需要调整最大尺寸 // 并查集数组 typedef struct { int parent[MAX_SIZE]; int count; }UnionFind; // 初始化并查集 void initialize(UnionFind* uf, int size) { for (int i = 0; i < size; i++) { uf->parent[i] = i; } uf->count = size; } // 查找根节点(代表元素) int find(UnionFind* uf, int x) { if (uf->parent[x] != x) { uf->parent[x] = find(uf, uf->parent[x]); // 路径压缩 } return uf->parent[x]; } // 合并两个元素所在的集合 void unionSets(UnionFind* uf, int x, int y) { //找到两个元素所在集合的代表元素 int xRoot = find(uf, x); int yRoot = find(uf, y); //如果代表元素不同,表明它们是两个集合,将它们合并 if (xRoot != yRoot) { uf->parent[xRoot] = yRoot; // uf->parent[yRoot] = xRoot; uf->count--; } } //判断图中是否存在路径,n 表示图中顶点的数量,edgs数组存储的是图中所有的边,len 表示边的数量,source 和 destination 表示要进行判断的两个顶点 bool validPath(int n, int(*edgs)[2], int len, int source, int destination) { UnionFind uf; initialize(&uf, n); // 把图存储到并查集中 for (int i = 0; i < len; i++) { unionSets(&uf, edgs[i][0], edgs[i][1]); } //判断 source 和 destination 是否同处一个集合 if (find(&uf, source) == find(&uf, destination)) { return true; } else { return false; } } // 主函数 int main() { int edgs[5][2] = { {0,1},{0,2},{3,5},{5,4},{4,3} }; //判断顶点 1 和 2 之间是否存在有效的路径 bool ret = validPath(6, edgs, 5, 1, 2); if (ret == true) { printf("顶点 1 和顶点 2 之间存在有效的路径\n"); } else { printf("顶点 1 和顶点 2 不存在有效的路径\n"); } //判断顶点 1 和 5 之间是否存在有效的路径 ret = validPath(6, edgs, 5, 1, 5); if (ret == true) { printf("顶点 1 和顶点 5 之间存在有效的路径\n"); } else { printf("顶点 1 和顶点 5 不存在有效的路径\n"); } return 0; }
下面是用并查集解决“图中是否存在路径”的 Java 程序:
// 并查集类,用于处理集合合并和查找操作 public class UnionFind { private int[] parent; // 记录节点的父节点 private int count; // 记录集合的数量 // 构造函数,初始化并查集,每个节点的父节点初始为自己,集合数量为节点个数 public UnionFind(int size) { parent = new int[size]; for (int i = 0; i < size; i++) { parent[i] = i; } count = size; } // 查找根节点(代表元素)的方法,同时进行路径压缩 public int find(int x) { if (parent[x] != x) { parent[x] = find(parent[x]); // 路径压缩 } return parent[x]; } // 合并两个元素所在的集合 public void unionSets(int x, int y) { int xRoot = find(x); int yRoot = find(y); if (xRoot != yRoot) { parent[xRoot] = yRoot; // 合并集合 count--; } } // 获取当前集合的数量 public int getCount() { return count; } } // 主程序类,用于测试并查集是否存在有效路径 public class ValidPath { // 判断图中是否存在路径的方法 public static boolean validPath(int n, int[][] edges, int len, int source, int destination) { UnionFind uf = new UnionFind(n); // 将图存储到并查集中 for (int i = 0; i < len; i++) { uf.unionSets(edges[i][0], edges[i][1]); } // 判断 source 和 destination 是否属于同一个集合 return uf.find(source) == uf.find(destination); } // 主函数,用于测试并查集是否能够判断有效路径 public static void main(String[] args) { int[][] edges = {{0, 1}, {0, 2}, {3, 5}, {5, 4}, {4, 3}}; // 判断顶点 1 和 2 之间是否存在有效的路径 boolean ret = validPath(6, edges, 5, 1, 2); if (ret) { System.out.println("顶点 1 和顶点 2 之间存在有效的路径"); } else { System.out.println("顶点 1 和顶点 2 不存在有效的路径"); } // 判断顶点 1 和 5 之间是否存在有效的路径 ret = validPath(6, edges, 5, 1, 5); if (ret) { System.out.println("顶点 1 和顶点 5 之间存在有效的路径"); } else { System.out.println("顶点 1 和顶点 5 不存在有效的路径"); } } }
下面是用并查集解决“图中是否存在路径”的 Python 程序:
class UnionFind: def __init__(self, size): # 初始化并查集,每个节点的父节点初始为自己,集合数量为节点个数 self.parent = [i for i in range(size)] self.count = size def find(self, x): # 查找根节点(代表元素)的方法,同时进行路径压缩 if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) # 路径压缩 return self.parent[x] def union_sets(self, x, y): # 合并两个元素所在的集合 x_root = self.find(x) y_root = self.find(y) if x_root != y_root: self.parent[x_root] = y_root # 合并集合 self.count -= 1 def get_count(self): # 获取当前集合的数量 return self.count def valid_path(n, edges, source, destination): uf = UnionFind(n) # 将图存储到并查集中 for edge in edges: uf.union_sets(edge[0], edge[1]) # 判断 source 和 destination 是否属于同一个集合 return uf.find(source) == uf.find(destination) if __name__ == "__main__": edges = [[0, 1], [0, 2], [3, 5], [5, 4], [4, 3]] # 判断顶点 1 和 2 之间是否存在有效的路径 ret = valid_path(6, edges, 1, 2) if ret: print("顶点 1 和顶点 2 之间存在有效的路径") else: print("顶点 1 和顶点 2 不存在有效的路径") # 判断顶点 1 和 5 之间是否存在有效的路径 ret = valid_path(6, edges, 1, 5) if ret: print("顶点 1 和顶点 5 之间存在有效的路径") else: print("顶点 1 和顶点 5 不存在有效的路径")
运行程序,输出结果为:
顶点 1 和顶点 2 之间存在有效的路径
顶点 1 和顶点 5 不存在有效的路径