目录
LT200. 岛屿数量
出题指数五颗星,出现频率极高
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成
此外,你可以假设该网格的四条边均被水包围
例子
输入:grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
输出:1
输入:grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
输出:3
这道题可以用广度优先搜索和深度优先搜索的方法来做。
首先对这两种搜索方法进行理解
(1)广度优先搜索bfs 会根据离起点的距离,按照从近到远的顺序对各节点进行搜索。
广度优先搜索中,有一个保存候补节点的队列,队列的性质就是先进先出,即先进入该队列的候补节点就先进行搜索。
借助例子解释一下:
下图中红色表示当前所在的节点(节点A),终点设为节点G,将与节点A直连的三个节点B, C, D放入存放候补节点的队列中(与节点A直连的三个节点放入时可以没有顺序,这里的放入顺序为B, C, D),用绿色表示:
此时队列有 [B, C, D],都是与A直接相连的。本着先进先出的原则,先把B弹出,然后找寻跟B相连的节点。跟B相连有E和F,所以把E和F录入,则此时队列为 [C,D,E,F], 继续下去,把C弹出,然后把C相连的节点H录入,此时队列是 [D, E, F, H], 还是没找到要找的G节点,于是继续弹出与录入,继续弹出D,录入与D相连的I和J,此时队列是 [E, F, H, I, J], 继续弹出E, 录入K, 得到[F, H, I, J, K], 弹出F,无录入节点,弹出H,录入G,此刻队列是[I, J, K, G]. 找到G节点。结束。
广度优先搜索为从起点开始,由近及远进行广泛的搜索。因此,目标节点离起点越近,搜索结束得就越快。
(2)深度优先搜索dfs 会沿着一条路径不断往下搜索直到不能再继续为止,然后再折返,开始搜索下一条路径。深度优先搜索中,保存候补节点是栈,栈的性质就是先进后出。
也是上面那个例子做解释
还是将起点设为节点A,终点设为节点G,先录入A相连的节点,假设录入的顺序是 D,C,B,栈是[D, C, B], 本着先进后出的原则。 先弹出的是B,录入的是E和F。此刻栈是 [D, C, F, E] 然后继续弹出E,录入K,栈为 [D, C, F, K], 继续弹出K,无相连节点,弹出F,无相连节点。此时栈为 [D, C], 弹出C,录入H,此时栈为 [D, H], 弹出H,录入G,此时找到G了。 结束。
上面👆就是两种方法的搜寻方式,一个优先搜索宽度,一个优先搜索深度。
回到LT题目。
(1)使用广度优先搜索方法-bfs
从坐标(0,0) 开始搜寻,如果是1的话,把其相邻坐标都搜做一遍,并把同样为1的坐标录入队列。队列的搜索方式先进先出。不断地弹出,录入内容为1的坐标,直到队列为空。
【录入队列的时候,会顺便把录入的坐标内容从1都会被重新标记为 0,表示已经搜索过,不需再重新搜索录入】
最后岛屿的数量就是我们进行广度优先搜索的次数
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
num_r = len(grid)
if num_r == 0:
return 0
num_c = len(grid[0])
ans = 0
for r in range(num_r):
for c in range(num_c):
if grid[r][c] == "1":
ans += 1
grid[r][c] = "0"
neighbors = collections.deque([(r, c)]) #把坐标入队列
while neighbors:
row, col = neighbors.popleft()
#先进先出,弹出去
for x, y in [(row - 1, col), (row + 1, col), (row, col - 1), (row, col + 1)]:
#对其邻边的内容进行搜索,如果内容为1,则进行录入,并修改为0
if 0 <= x < num_r and 0 <= y < num_c and grid[x][y] == "1":
neighbors.append((x, y))
grid[x][y] = "0"
return ans
时间复杂度:O(MN); 空间复杂度:O(min(M,N))
(2)使用深度优先搜索-dfs
同理,dfs也是差不多的原理,只不过dfs会针对一个节点,不断滴进行探索,直到没有内容为1的坐标。
即如果一个位置为 11,则以其为起始节点开始进行深度优先搜索。并且在搜索的过程中,每个搜索过的1都会被重新标记为 0。
class Solution:
def dfs(self, grid, r, c):
#把搜索过的坐标内容重新标识为0
grid[r][c] = 0
num_c = len(grid)
if num_c ==0:return 0
num_r = len(grid[0])
for x,y in [(r-1,c),(r+1,c),(r,c-1),(r,c+1)]:
if 0<=x<num_c and 0<= y < num_r and grid[x][y] =='1':
self.dfs(grid,x,y)
def numIslands(self, grid: List[List[str]]) -> int:
num_c = len(grid)
if num_c ==0:return 0
num_r = len(grid[0])
ans = 0
for c in range(num_c):
for r in range(num_r):
if grid[c][r] == '1':
ans+= 1
self.dfs(grid,c,r)
return ans
时间复杂度:O(MN)
空间复杂度:O(MN)