题目

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

image.png

思路

遍历所有位置,每个位置利用DFS去扫描岛屿边界。

  1. class Solution:
  2. def numIslands(self, grid: List[List[str]]) -> int:
  3. if not grid or len(grid) == 0: return 0
  4. rows, cols = len(grid), len(grid[0])
  5. count = 0
  6. for row in range(rows):
  7. for col in range(cols):
  8. if grid[row][col] == '1':
  9. count += 1
  10. self.dfs(grid, row, col)
  11. return count
  12. def dfs(self, grid, row, col):
  13. if row < 0 or row >= len(grid) or col < 0 or col >= len(grid[0]) or grid[row][col] != '1':
  14. return
  15. else:
  16. grid[row][col] = '2' # 用'2'标记已经访问的位置
  17. # 探索上下左右四个方向
  18. self.dfs(grid, row - 1, col) # 上
  19. self.dfs(grid, row + 1, col) # 下
  20. self.dfs(grid, row, col - 1) # 左
  21. self.dfs(grid, row, col + 1) # 右

深度优先搜索

  1. from typing import List
  2. class Solution:
  3. # x-1,y
  4. # x,y-1 x,y x,y+1
  5. # x+1,y
  6. # 方向数组,它表示了相对于当前位置的 4 个方向的横、纵坐标的偏移量,这是一个常见的技巧
  7. directions = [(-1, 0), (0, -1), (1, 0), (0, 1)]
  8. def numIslands(self, grid: List[List[str]]) -> int:
  9. m = len(grid)
  10. # 特判
  11. if m == 0:
  12. return 0
  13. n = len(grid[0])
  14. marked = [[False for _ in range(n)] for _ in range(m)]
  15. count = 0
  16. # 从第 1 行、第 1 格开始,对每一格尝试进行一次 DFS 操作
  17. for i in range(m):
  18. for j in range(n):
  19. # 只要是陆地,且没有被访问过的,就可以使用 DFS 发现与之相连的陆地,并进行标记
  20. if not marked[i][j] and grid[i][j] == '1':
  21. # count 可以理解为连通分量,你可以在深度优先遍历完成以后,再计数,
  22. self.__dfs(grid, i, j, m, n, marked)
  23. count += 1
  24. return count
  25. def __dfs(self, grid, i, j, m, n, marked):
  26. marked[i][j] = True
  27. for direction in self.directions:
  28. new_i = i + direction[0]
  29. new_j = j + direction[1]
  30. if 0 <= new_i < m and 0 <= new_j < n and not marked[new_i][new_j] and grid[new_i][new_j] == '1':
  31. self.__dfs(grid, new_i, new_j, m, n, marked)
  32. if __name__ == '__main__':
  33. grid = [['1', '1', '1', '1', '0'],
  34. ['1', '1', '0', '1', '0'],
  35. ['1', '1', '0', '0', '0'],
  36. ['0', '0', '0', '0', '0']]
  37. solution = Solution()
  38. result = solution.numIslands(grid)
  39. print(result)

广度优先搜索

为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 11,则将其加入队列,开始进行广度优先搜索。

  1. from typing import List
  2. from collections import deque
  3. class Solution:
  4. # x-1,y
  5. # x,y-1 x,y x,y+1
  6. # x+1,y
  7. # 方向数组,它表示了相对于当前位置的 4 个方向的横、纵坐标的偏移量,这是一个常见的技巧
  8. directions = [(-1, 0), (0, -1), (1, 0), (0, 1)]
  9. def numIslands(self, grid: List[List[str]]) -> int:
  10. m = len(grid)
  11. # 特判
  12. if m == 0:
  13. return 0
  14. n = len(grid[0])
  15. marked = [[False for _ in range(n)] for _ in range(m)]
  16. count = 0
  17. # 从第 1 行、第 1 格开始,对每一格尝试进行一次 DFS 操作
  18. for i in range(m):
  19. for j in range(n):
  20. # 只要是陆地,且没有被访问过的,就可以使用 BFS 发现与之相连的陆地,并进行标记
  21. if not marked[i][j] and grid[i][j] == '1':
  22. # count 可以理解为连通分量,你可以在广度优先遍历完成以后,再计数,
  23. # 即这行代码放在【位置 1】也是可以的
  24. count += 1
  25. queue = deque()
  26. queue.append((i, j))
  27. # 注意:这里要标记上已经访问过
  28. marked[i][j] = True
  29. while queue:
  30. cur_x, cur_y = queue.popleft()
  31. # 得到 4 个方向的坐标
  32. for direction in self.directions:
  33. new_i = cur_x + direction[0]
  34. new_j = cur_y + direction[1]
  35. # 如果不越界、没有被访问过、并且还要是陆地,我就继续放入队列,放入队列的同时,要记得标记已经访问过
  36. if 0 <= new_i < m and 0 <= new_j < n and not marked[new_i][new_j] and grid[new_i][new_j] == '1':
  37. queue.append((new_i, new_j))
  38. #【特别注意】在放入队列以后,要马上标记成已经访问过,语义也是十分清楚的:反正只要进入了队列,你迟早都会遍历到它
  39. # 而不是在出队列的时候再标记
  40. #【特别注意】如果是出队列的时候再标记,会造成很多重复的结点进入队列,造成重复的操作,这句话如果你没有写对地方,代码会严重超时的
  41. marked[new_i][new_j] = True
  42. #【位置 1】
  43. return count
  44. if __name__ == '__main__':
  45. grid = [['1', '1', '1', '1', '0'],
  46. ['1', '1', '0', '1', '0'],
  47. ['1', '1', '0', '0', '0'],
  48. ['0', '0', '0', '0', '0']]
  49. solution = Solution()
  50. result = solution.numIslands(grid)
  51. print(result)
  52. 作者:liweiwei1419
  53. 链接:https://leetcode-cn.com/problems/number-of-islands/solution/dfs-bfs-bing-cha-ji-python-dai-ma-java-dai-ma-by-l/
  54. 来源:力扣(LeetCode
  55. 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。