diff --git a/content/posts/leetcode.md b/content/posts/leetcode.md index c2b5931..4ab0293 100644 --- a/content/posts/leetcode.md +++ b/content/posts/leetcode.md @@ -3836,3 +3836,125 @@ func findFarmland(land [][]int) [][]int { return result } ``` + +## day54 2024-04-21 + +### 1971. Find if Path Exists in Graph + +There is a bi-directional graph with n vertices, where each vertex is labeled from 0 to n - 1 (inclusive). The edges in the graph are represented as a 2D integer array edges, where each edges[i] = [ui, vi] denotes a bi-directional edge between vertex ui and vertex vi. Every vertex pair is connected by at most one edge, and no vertex has an edge to itself. + +You want to determine if there is a valid path that exists from vertex source to vertex destination. + +Given edges and the integers n, source, and destination, return true if there is a valid path from source to destination, or false otherwise. + +![04213IWGw1wdQ1vc](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/04213IWGw1wdQ1vc.png) + +### 题解 + +本题使用并查集即可快速解决, 因为若两个节点连通, 两个节点必定位于同一个连通图中, 而每个连通图可以通过并查集使用一个节点的值来表示, 因此遍历所有边并构造并查集, 最终比较源点和目标点所在的并查集的代表元(即用来代表一个连通图的节点的值)是否相同即可确定是否有通路. + +### 代码 + +```go +func validPath(n int, edges [][]int, source int, destination int) bool { + querymap := map[int]int{} + querymap[source] = source + querymap[destination] = destination + for _,edge := range edges{ + value,exist := querymap[edge[0]] + value2,exist2 := querymap[edge[1]] + if !exist && !exist2{ + querymap[edge[0]] = edge[0] + querymap[edge[1]] = edge[0] + }else if exist && !exist2{ + querymap[edge[1]] = value + }else if !exist && exist2{ + querymap[edge[0]] = value2 + }else{ + for querymap[value] != value{ + value = querymap[value] + } + for querymap[value2] != value2{ + value2 = querymap[value2] + } + if value != value2{ + querymap[value2] = value + } + } + } + + for querymap[source] != source{ + source = querymap[source] + } + for querymap[destination] != destination{ + destination = querymap[destination] + } + if source != destination{ + return false + }else{ + return true + } +} +``` + +### 总结 + +最快的解法同样使用了并查集, 不过将并查集的操作都单独写成了对应的函数. 同时使用了数组来保存集合中某个元素的父元素是什么, 数组下标表示某个节点, 对应的值表示其父节点的值. 这样查询速度更快, 不过浪费了一些空间. + +```go +type DisjointSet struct { + roots []int + ranks []int +} + +func (ds *DisjointSet) Find(x int) int { + if ds.roots[x] == x { + return x + } + ds.roots[x] = ds.Find(ds.roots[x]) + return ds.roots[x] +} + +func (ds *DisjointSet) Union(x, y int) { + rootX := ds.Find(x) + rootY := ds.Find(y) + if rootX == rootY { + return + } + rankX := ds.ranks[rootX] + rankY := ds.ranks[rootY] + if rankX > rankY { + ds.roots[rootY] = rootX + return + } + if rankX < rankY { + ds.roots[rootX] = rootY + return + } + ds.roots[rootX] = rootY + ds.ranks[rootY] += 1 +} + +func (ds *DisjointSet) IsConnected(x, y int) bool { + return ds.Find(x) == ds.Find(y) +} + +func newDisjointSet(n int) *DisjointSet { + roots := make([]int, n) + ranks := make([]int, n) + for i := range n { + roots[i] = i + ranks[i] = 1 + } + newDisjointSet := DisjointSet{roots, ranks} + return &newDisjointSet +} + +func validPath(n int, edges [][]int, source int, destination int) bool { + ds := newDisjointSet(n) + for _, edge := range edges { + ds.Union(edge[0], edge[1]) + } + return ds.IsConnected(source, destination) +} +```