并查集(union-find) https://labuladong.gitee.io/algo/2/20/41/
基本实现 用一个类 UF 来实现并查集。初始化时,将每个节点的父节点设为自己。
union
操作将 p 的根节点接到 q 的根节点下面。
find
操作查找节点 x 的根节点,且在查找的路上将路径压缩,将 x 直接接到它的爷爷节点(父节点的父节点)下面,这样下来最终树的高度不会超过 3。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 type UF struct { count int parents []int }func NewUF (n int ) UF { parents := make ([]int , n) for i := 0 ; i < n; i++ { parents[i] = i } return UF{n, parents} }func (u *UF) Union (p, q int ) { rootP, rootQ := u.Find(p), u.Find(q) if rootP == rootQ { return } u.parents[rootP] = rootQ u.count -= 1 }func (u *UF) Find (x int ) int { for u.parents[x] != x { u.parents[x] = u.parents[u.parents[x]] x = u.parents[x] } return x }func (u *UF) Connected (p, q int ) bool { rootP, rootQ := u.Find(p), u.Find(q) return rootP == rootQ }
被围绕的区域 https://leetcode-cn.com/problems/surrounded-regions/
这个问题有个重要的性质:和 边界上的 0 连通的 0 不会被替换,而其他的 0 要被替换。
因此,利用并查集,我们将边界上的所有 0 和图一个 dummy
根节点相连,然后遍历棋盘内部,将所有的 0 来跟周围的 0 连接。最后判断一个 0 是否要被替换,就看它的根节点是否为 dummy
,如果是就不用替换。
实现要点:
为 dummy
根节点预留位置
将二维数组转为一维的技巧:(x, y) => x*n + y
(n 为列数)
为了不破坏上面这个映射结构,dummy
的位置为数组最末尾
用 directions
数组技巧来遍历上下左右四个位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 func solve (board [][]byte ) { m, n := len (board), len (board[0 ]) uf := NewUF(m * n + 1 ) dummy := m * n for row := 0 ; row < m; row++ { if board[row][0 ] == 'O' { uf.Union(row * n, dummy) } if board[row][n-1 ] == 'O' { uf.Union(row * n + n - 1 , dummy) } } for col := 0 ; col < n; col++ { if board[0 ][col] == 'O' { uf.Union(col, dummy) } if board[m-1 ][col] == 'O' { uf.Union((m-1 ) * n + col, dummy) } } directions := [][]int { {1 , 0 }, {0 , 1 }, {0 , -1 }, {-1 , 0 }, } for i := 1 ; i < m-1 ; i++ { for j := 1 ; j < n-1 ; j++ { if board[i][j] == 'O' { for _, d := range directions { x := i + d[0 ] y := j + d[1 ] if board[x][y] == 'O' { uf.Union(i * n + j, x * n + y) } } } } } for i := 1 ; i < m-1 ; i++ { for j := 1 ; j < n-1 ; j++ { if !uf.Connected(dummy, i * n + j) { board[i][j] = 'X' } } } }
注:这题更好的解法是遍历边界上的 0,用 DFS 或 BFS 将与它们连接的 0 替换成别的一个特殊字符 #,然后替换掉其他所有的 0,再将 # 替换回来。
等式方程的可满足性 https://leetcode-cn.com/problems/satisfiability-of-equality-equations/
用 ==
连接的变量,就是互相连通的关系,可以自然地用并查集解决。具体来说,先处理 ==
连接的变量,构造连通图,然后处理 !=
的变量,检查是否破坏了连通关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func equationsPossible (equations []string ) bool { uf := NewUF(26 ) for _, eq := range equations { if eq[1 ] == '=' { uf.Union(int (eq[0 ] - 'a' ), int (eq[3 ] - 'a' )) } } for _, eq := range equations { if eq[1 ] == '!' { if uf.Connected(int (eq[0 ] - 'a' ), int (eq[3 ] - 'a' )) { return false } } } return true }