LeetCode_并查集_中等_130.被围绕的区域

x33g5p2x  于2022-05-05 转载在 其他  
字(2.3k)|赞(0)|评价(0)|浏览(545)

1.题目

给你一个 m x n 的矩阵 board ,由若干字符 ‘X’ 和 ‘O’ ,找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充

示例 1:

输入:board = [[“X”,“X”,“X”,“X”],[“X”,“O”,“O”,“X”],[“X”,“X”,“O”,“X”],[“X”,“O”,“X”,“X”]]
输出:[[“X”,“X”,“X”,“X”],[“X”,“X”,“X”,“X”],[“X”,“X”,“X”,“X”],[“X”,“O”,“X”,“X”]]
解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的 ‘O’ 都不会被填充为 ‘X’。 任何不在边界上,或不与边界上的 ‘O’ 相连的 ‘O’ 最终都会被填充为 ‘X’。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

示例 2:
输入:board = “X”
输出:“X”

提示:
m == board.length
n == board[i].length
1 <= m, n <= 200
board[i][j] 为 ‘X’ 或 ‘O’

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/surrounded-regions

2.思路

(1)并查集
本题和 LeetCode_DFS_中等_1254. 统计封闭岛屿的数目 这题几乎完全一样,常规做法使用 DFS,这里我们使用另一种算法——并查集算法。并查集的思路参考并查集(UNION-FIND)算法详解

3.代码实现(Java)

  1. //思路1————并查集
  2. class Solution {
  3. public void solve(char[][] board) {
  4. // m 行 n 列的矩阵
  5. int m = board.length;
  6. int n = board[0].length;
  7. /*
  8. 由题分析可知,与矩阵边界相邻的 O 不需要被替换
  9. 假设它们有一个共同根节点叫 dummy,这些 O 和 dummy 互相连通,而那些需要被替换的 O 与 dummy 不连通。
  10. */
  11. //给 dummy 留一个额外位置
  12. UF uf = new UF(m * n + 1);
  13. int dummy = m * n;
  14. //将首列和末列的 O 与 dummy 连通
  15. for (int i = 0; i < m; i++) {
  16. if (board[i][0] == 'O')
  17. uf.union(i * n, dummy);
  18. if (board[i][n - 1] == 'O')
  19. uf.union(i * n + n - 1, dummy);
  20. }
  21. //将首行和末行的 O 与 dummy 连通
  22. for (int j = 0; j < n; j++) {
  23. if (board[0][j] == 'O') {
  24. uf.union(j, dummy);
  25. }
  26. if (board[m - 1][j] == 'O') {
  27. uf.union(n * (m - 1) + j, dummy);
  28. }
  29. }
  30. //设置方向数组 d
  31. int[][] d = new int[][]{{1, 0}, {0, 1}, {0, -1}, {-1, 0}};
  32. for (int i = 1; i < m - 1; i++) {
  33. for (int j = 1; j < n - 1; j++) {
  34. if (board[i][j] == 'O') {
  35. //将此 'O' 与其上下左右的 'O' 连通
  36. for (int k = 0; k < 4; k++) {
  37. int x = i + d[k][0];
  38. int y = j + d[k][1];
  39. if (board[x][y] == 'O') {
  40. uf.union(x * n + y, i * n + j);
  41. }
  42. }
  43. }
  44. }
  45. }
  46. //替换掉所有不与 dummy 相通的 'O'
  47. for (int i = 1; i < m - 1; i++) {
  48. for (int j = 1; j < n; j++) {
  49. if (!uf.isConnected(dummy, i * n + j)) {
  50. board[i][j] = 'X';
  51. }
  52. }
  53. }
  54. }
  55. }
  56. //并查集
  57. class UF {
  58. //记录连通分量(树)的个数
  59. private int count;
  60. //节点 x 的根节点是 root[x]
  61. private int[] root;
  62. //记录每棵树中的节点数
  63. private int[] size;
  64. //初始化
  65. public UF(int n) {
  66. //初始时每个节点都是一个连通分量
  67. this.count = n;
  68. root = new int[n];
  69. size = new int[n];
  70. for (int i = 0; i < n; i++) {
  71. //初始时每个节点的根节点都是其自己
  72. root[i] = i;
  73. size[i] = 1;
  74. }
  75. }
  76. //将 p 和 q 连通
  77. public void union(int p, int q) {
  78. int rootP = find(p);
  79. int rootQ = find(q);
  80. if (rootP == rootQ) {
  81. // p 和 q 的根节点相同,它们本就是连通的,直接返回即可
  82. return;
  83. } else {
  84. /*
  85. p 和 q 的根节点不相同,将它们连通
  86. 小树接到大树下面,这样比较平衡
  87. */
  88. if (size[rootP] > size[rootQ]) {
  89. root[rootQ] = rootP;
  90. size[rootP] += size[rootQ];
  91. } else {
  92. root[rootP] = rootQ;
  93. size[rootQ] += size[rootP];
  94. }
  95. count--;
  96. }
  97. }
  98. //判断 p 和 q 是否互相连通
  99. public boolean isConnected(int p, int q) {
  100. int rootP = find(p);
  101. int rootQ = find(q);
  102. //如果 p 和 q 的根节点相同,则说明它们在同一颗树上,即它们是连通的
  103. return rootP == rootQ;
  104. }
  105. //查找节点 x 的根节点
  106. public int find(int x) {
  107. while (root[x] != x) {
  108. //进行路径压缩
  109. root[x] = root[root[x]];
  110. x = root[x];
  111. }
  112. return x;
  113. }
  114. //返回连通分量(树)的个数
  115. public int getCount() {
  116. return count;
  117. }
  118. }

相关文章