题目
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
思路
遍历所有位置,每个位置利用DFS去扫描岛屿边界。
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
if not grid or len(grid) == 0: return 0
rows, cols = len(grid), len(grid[0])
count = 0
for row in range(rows):
for col in range(cols):
if grid[row][col] == '1':
count += 1
self.dfs(grid, row, col)
return count
def dfs(self, grid, row, col):
if row < 0 or row >= len(grid) or col < 0 or col >= len(grid[0]) or grid[row][col] != '1':
return
else:
grid[row][col] = '2' # 用'2'标记已经访问的位置
# 探索上下左右四个方向
self.dfs(grid, row - 1, col) # 上
self.dfs(grid, row + 1, col) # 下
self.dfs(grid, row, col - 1) # 左
self.dfs(grid, row, col + 1) # 右
深度优先搜索
from typing import List
class Solution:
# x-1,y
# x,y-1 x,y x,y+1
# x+1,y
# 方向数组,它表示了相对于当前位置的 4 个方向的横、纵坐标的偏移量,这是一个常见的技巧
directions = [(-1, 0), (0, -1), (1, 0), (0, 1)]
def numIslands(self, grid: List[List[str]]) -> int:
m = len(grid)
# 特判
if m == 0:
return 0
n = len(grid[0])
marked = [[False for _ in range(n)] for _ in range(m)]
count = 0
# 从第 1 行、第 1 格开始,对每一格尝试进行一次 DFS 操作
for i in range(m):
for j in range(n):
# 只要是陆地,且没有被访问过的,就可以使用 DFS 发现与之相连的陆地,并进行标记
if not marked[i][j] and grid[i][j] == '1':
# count 可以理解为连通分量,你可以在深度优先遍历完成以后,再计数,
self.__dfs(grid, i, j, m, n, marked)
count += 1
return count
def __dfs(self, grid, i, j, m, n, marked):
marked[i][j] = True
for direction in self.directions:
new_i = i + direction[0]
new_j = j + direction[1]
if 0 <= new_i < m and 0 <= new_j < n and not marked[new_i][new_j] and grid[new_i][new_j] == '1':
self.__dfs(grid, new_i, new_j, m, n, marked)
if __name__ == '__main__':
grid = [['1', '1', '1', '1', '0'],
['1', '1', '0', '1', '0'],
['1', '1', '0', '0', '0'],
['0', '0', '0', '0', '0']]
solution = Solution()
result = solution.numIslands(grid)
print(result)
广度优先搜索
为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 11,则将其加入队列,开始进行广度优先搜索。
from typing import List
from collections import deque
class Solution:
# x-1,y
# x,y-1 x,y x,y+1
# x+1,y
# 方向数组,它表示了相对于当前位置的 4 个方向的横、纵坐标的偏移量,这是一个常见的技巧
directions = [(-1, 0), (0, -1), (1, 0), (0, 1)]
def numIslands(self, grid: List[List[str]]) -> int:
m = len(grid)
# 特判
if m == 0:
return 0
n = len(grid[0])
marked = [[False for _ in range(n)] for _ in range(m)]
count = 0
# 从第 1 行、第 1 格开始,对每一格尝试进行一次 DFS 操作
for i in range(m):
for j in range(n):
# 只要是陆地,且没有被访问过的,就可以使用 BFS 发现与之相连的陆地,并进行标记
if not marked[i][j] and grid[i][j] == '1':
# count 可以理解为连通分量,你可以在广度优先遍历完成以后,再计数,
# 即这行代码放在【位置 1】也是可以的
count += 1
queue = deque()
queue.append((i, j))
# 注意:这里要标记上已经访问过
marked[i][j] = True
while queue:
cur_x, cur_y = queue.popleft()
# 得到 4 个方向的坐标
for direction in self.directions:
new_i = cur_x + direction[0]
new_j = cur_y + direction[1]
# 如果不越界、没有被访问过、并且还要是陆地,我就继续放入队列,放入队列的同时,要记得标记已经访问过
if 0 <= new_i < m and 0 <= new_j < n and not marked[new_i][new_j] and grid[new_i][new_j] == '1':
queue.append((new_i, new_j))
#【特别注意】在放入队列以后,要马上标记成已经访问过,语义也是十分清楚的:反正只要进入了队列,你迟早都会遍历到它
# 而不是在出队列的时候再标记
#【特别注意】如果是出队列的时候再标记,会造成很多重复的结点进入队列,造成重复的操作,这句话如果你没有写对地方,代码会严重超时的
marked[new_i][new_j] = True
#【位置 1】
return count
if __name__ == '__main__':
grid = [['1', '1', '1', '1', '0'],
['1', '1', '0', '1', '0'],
['1', '1', '0', '0', '0'],
['0', '0', '0', '0', '0']]
solution = Solution()
result = solution.numIslands(grid)
print(result)
作者:liweiwei1419
链接:https://leetcode-cn.com/problems/number-of-islands/solution/dfs-bfs-bing-cha-ji-python-dai-ma-java-dai-ma-by-l/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。