• 【LeetCode】最短的桥 [M](宽度优先遍历)


    934. 最短的桥 - 力扣(LeetCode)

    一、题目

    给你一个大小为 n x n 的二元矩阵 grid ,其中 1 表示陆地,0 表示水域。

    岛 是由四面相连的 1 形成的一个最大组,即不会与非组内的任何其他 1 相连。grid 中 恰好存在两座岛 。

    你可以将任意数量的 0 变为 1 ,以使两座岛连接起来,变成 一座岛 。

    返回必须翻转的 0 的最小数目。

    示例 1:

    输入:grid = [[0,1],[1,0]]
    输出:1

    示例 2:​​​​​​​

    输入:grid = [[0,1,0],[0,0,0],[0,0,1]]
    输出:2

    示例 3:​​​​​​​

    输入:grid = [[1,1,1,1,1],[1,0,0,0,1],[1,0,1,0,1],[1,0,0,0,1],[1,1,1,1,1]]
    输出:1

    提示:

    • n == grid.length == grid[i].length
    • 2 <= n <= 100
    • grid[i][j] 为 0 或 1
    • grid 中恰有两个岛

    二、代码

    1. class Solution {
    2. public int shortestBridge(int[][] grid) {
    3. // 矩阵的行数
    4. int n = grid.length;
    5. // 矩阵的列数
    6. int m = grid.length;
    7. // 矩阵一共有几个位置
    8. int all = n * m;
    9. // 将矩阵二维坐标转化为一维坐标,然后记录每个位置到最初连成一片的1的距离是多少
    10. // distanceRecord[0][i]:表示矩阵中的二维坐标转化为一维坐标i,这个位置到0号的那一片1距离是多少
    11. // distanceRecord[1][i]:表示矩阵中的二维坐标转化为一维坐标i,这个位置到1号的那一片1距离是多少
    12. // 一共就两片1,所以第一维就开两个空间即可。
    13. int[][] distanceRecord = new int[2][all];
    14. // 记录当前一轮宽度优先遍历要遍历的位置
    15. // curs[i] = v:v表示的是二维转一维的下标
    16. int[] curs = new int[all];
    17. // 记录下一轮宽度优先遍历要遍历的位置
    18. int[] nexts = new int[all];
    19. // 当前要统计到哪一片1的距离,一共就两片1,所以islandNo只会是0或1
    20. int islandNo = 0;
    21. // 遍历整个矩阵,去找最初始的两片1
    22. for (int i = 0; i < n; i++) {
    23. for (int j = 0; j < m; j++) {
    24. // 下面这个if分支在整个循环过程中只会进入两次
    25. // 如果当前遍历的位置是1,说明找到了一片1,进入到分支中
    26. if (grid[i][j] == 1) {
    27. // 首先要进行初始化,将record和curs初始化出来,将这一片1中的位置的距离都设置为1,存入到record,也就相当于标记他们已经被遍历过了
    28. // 然后还要将这一片1都加入到curs队列中,表示第一轮宽度优先遍历要从curs中的这些位置开始向外遍历一层
    29. // 返回值是当前curs已经填充到的位置,也可以认为是此时curs队列中有效数据的数量
    30. int cursIndex = infect(n, m, i, j, grid, 0, distanceRecord[islandNo], curs);
    31. // 表示当前这一轮宽度优先遍历要设置的到islandNo这一片1的距离是多少,在下面每一轮宽度优先遍历都要将distance++
    32. int distance = 2;
    33. // 开始进行宽度优先遍历,只要curs还有有效数据,就说明当前还有位置要宽度优先遍历
    34. while (cursIndex != 0) {
    35. // 宽度优先遍历,返回的是nexts队列已经填充到的位置,也可以认为是此时nexts队列中有效数据的数量
    36. int nextsIndex = bfs(n, m, grid, distanceRecord[islandNo], curs, cursIndex, nexts, distance++);
    37. // 当前nexts队列在下一轮宽度优先遍历的时候就就会变成下一轮的curs
    38. // 下面的就是要将nexts的地址赋值给curs上,这里要注意还要讲curs的地址赋值给next,就相当于两个引用交换了指向的地址空间
    39. // 如果不交换,只将nexts赋值给curs,就会导致最后curs和nexts指向的是同一个地址空间,就会出现错误
    40. int[] temp = curs;
    41. curs = nexts;
    42. nexts = temp;
    43. // 将nextsIndex也赋值给cursIndex
    44. cursIndex = nextsIndex;
    45. }
    46. // 将islandNo加1,当下一次再次进入到这个循环之后,就是另一片1了
    47. islandNo++;
    48. }
    49. }
    50. }
    51. // 完成了循环之后,distanceRecord中就存储了所有位置到那两片1的距离是多少
    52. // 找到到两片1距离的加和最小的位置
    53. int min = Integer.MAX_VALUE;
    54. for (int i = 0; i < distanceRecord[0].length; i++) {
    55. // 就计算当前位置i到两片1的距离加和,看能否将min推低,找到其中的最小值
    56. min = Math.min(distanceRecord[0][i] + distanceRecord[1][i], min);
    57. }
    58. // 因为统计出来重复的,重复了1个,然后又因为我们的距离是从1开始算的,其实也是多算了1个,所以最后要减3
    59. return min - 3;
    60. }
    61. // 将这一片1都感染为2,并且初始化curs队列和distanceRecord。就是一个宽度优先遍历的感染过程,当这个过程完成之后,这一片1就都会设置为2
    62. // 当前来到grid[i][j] , 总行数是N,总列数是M
    63. // grid[i][j]感染出去(找到这一片岛所有的1),把每一个1的坐标,放入到int[] curs队列!
    64. // 1 (a,b) -> curs[index++] = (a * M + b)
    65. // 1 (c,d) -> curs[index++] = (c * M + d)
    66. // 二维已经变成一维了, 1 (a,b) -> a * M + b
    67. // 设置距离record[a * M +b ] = 1,默认初始化的距离都是1
    68. public int infect(int n, int m, int i, int j, int[][] grid, int index, int[] distanceRecord, int[] curs) {
    69. // 保证当前位置不越界,并且这个位置在矩阵中的值为1
    70. if (i < 0 || i >= n || j < 0 || j >= m || grid[i][j] != 1) {
    71. return index;
    72. }
    73. // m[i][j] 不越界,且m[i][j] == 1
    74. // 将其感染为2,这就保证了shortestBridge()方法中的循环中的if分支只会进入到两次,因为当这个过程完成之后,这一片1就都会设置为2,后面即使遍历到了这一片1的位置也不会进入到那个if分支了
    75. grid[i][j] = 2;
    76. // 当前位置二维坐标转化为一维坐标
    77. int curIndex = i * m + j;
    78. // 将当前位置的距离设置为1
    79. distanceRecord[curIndex] = 1;
    80. // 将当前位置加入到curs队列
    81. curs[index++] = curIndex;
    82. // 注意下面的过程是依次执行的,所以在执行过程中是要更新index的,因为每一次都有可能将新的位置加入到curs中,推高index的指向
    83. // 所以每一次执行传入的index也都是最新的
    84. index = infect(n, m, i + 1, j, grid, index, distanceRecord, curs);
    85. index = infect(n, m, i - 1, j, grid, index, distanceRecord, curs);
    86. index = infect(n, m, i, j + 1, grid, index, distanceRecord, curs);
    87. index = infect(n, m, i, j - 1, grid, index, distanceRecord, curs);
    88. // 返回当前curs已经填充到的位置
    89. return index;
    90. }
    91. // 宽度优先遍历,本轮宽度优先遍历是从curs中的位置开始,然后向外遍历一层,向外遍历到的新位置就会加入到nexts中,下一轮的宽度优先遍历就要从nexts中的位置开始
    92. // 二维原始矩阵中,N总行数,M总列数
    93. // distance:当前要遍历到的这一层到那一片1的距离
    94. // cursSize:当前curs队列中的有效位置数量
    95. // curs:curs中存储的是位置
    96. // record里面拿距离,如果遍历到的位置对应的record值不是0,说明这个位置已经遍历过了,直接跳过
    97. public int bfs(int n, int m, int[][] grid, int[] distanceRecord, int[] curs, int cursSize, int[] nexts, int distance) {
    98. // 从nexts的0位置开始填充
    99. int nextsIndex = 0;
    100. // 遍历curs中的位置,开始宽度优先遍历
    101. for (int i = 0; i < cursSize; i++) {
    102. // 向当前位置左右上下都遍历一层,先判断位置合法性
    103. // 先去判断当前位置的左右上下是否还有位置,如果没有了,就设置为-1,如果有就计算出对应的位置。
    104. int leftIndex = curs[i] % m == 0 ? -1 : curs[i] - 1;
    105. int rightIndex = curs[i] % m == m - 1 ? -1 : curs[i] + 1;
    106. int upIndex = curs[i] / m == 0 ? -1 : curs[i] - m;
    107. int downIndex = curs[i] / m == n - 1 ? -1 : curs[i] + m;
    108. // 如果确实有左右上下的位置,并且该位置没有被遍历过(distanceRecord[leftIndex] == 0)
    109. if (leftIndex != -1 && distanceRecord[leftIndex] == 0) {
    110. // 设置该位置到那一片1的距离
    111. distanceRecord[leftIndex] = distance;
    112. // 将该位置加入到nexts中
    113. nexts[nextsIndex++] = leftIndex;
    114. }
    115. if (rightIndex != -1 && distanceRecord[rightIndex] == 0) {
    116. distanceRecord[rightIndex] = distance;
    117. nexts[nextsIndex++] = rightIndex;
    118. }
    119. if (upIndex != -1 && distanceRecord[upIndex] == 0) {
    120. distanceRecord[upIndex] = distance;
    121. nexts[nextsIndex++] = upIndex;
    122. }
    123. if (downIndex != -1 && distanceRecord[downIndex] == 0) {
    124. distanceRecord[downIndex] = distance;
    125. nexts[nextsIndex++] = downIndex;
    126. }
    127. }
    128. // 返回nexts填充到的位置
    129. return nextsIndex;
    130. }
    131. }

    三、解题思路 

    这道题就是进行两遍宽度优先遍历,将所有位置到两片1的距离都计算出来,然后找到到两片1距离加和最小的,就是我们想要的答案。

  • 相关阅读:
    【Python 之 Numpy】创建数组
    【AcWing 学习】数据结构 + STL
    华为U8818从系统android 4.06降级为android 2.3.6攻略
    【论文阅读】YOLO-World | 开集目标检测
    什么神仙笔记!阿里P9用39实例+1项目讲明白了Spring Cloud家族
    MySQl表的增删查改(CRUD)
    ARM的工作模式
    信息学奥赛一本通 1435:【例题3】曲线 | 洛谷 洛谷 P1883 函数
    论文指标评价体系及权重计算
    Redis基本数据结构及底层实现原理
  • 原文地址:https://blog.csdn.net/cy973071263/article/details/127869089