以岛屿问题为代表的网格类DFS问题(Python)
问题链接
参考链接:力扣
在 LeetCode 中,「岛屿问题」是一个系列系列问题,比如:
以上是我目前做到的岛屿类题目系列题。
以上的解题方法都是基于一个模板:
将基于树的DFS扩展到基于网格的DFS
基于树的DFS基本模板是:
def dfs(root):
# 终止条件
if not root:
return
dfs(root.left)
dfs(root.right)
二叉树只需递归调用左孩子和右孩子即可
而在网格图中,有上下左右(4个方向),有的题目中还有对角线(8个方向)
但是无论多少个方向,我们解题的大致思路都是一致的,即:
def dfs(grid, r, c):
# 判断是否超出网格边界
if not(r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0])):
return
# 判断是否是岛屿(陆地)
if grid[r][c] != 1:
return
grid[r][c] = 2
dfs(grid, r - 1, c)
dfs(grid, r + 1, c)
dfs(grid, r, c - 1)
dfs(grid, r, c + 1)
岛屿的最大面积 与 岛屿周长问题详解可以参考顶部链接。 这里仅附上python代码
最大岛屿面积
class Solution(object):
def maxAreaOfIsland(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
def dfs(grid, r, c):
# 判定边界
if not (r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0])):
return 0
# return 返回的值有时候是 空 有时候是具体值,需要根据题意判定
if grid[r][c] != 1: # 如果不是陆地或者已经标记过的陆地也跳过
return 0
grid[r][c] = 2 # 标记r,c处的陆地
return 1 + dfs(grid, r - 1, c) + dfs(grid, r + 1, c) + dfs(grid, r, c + 1) + dfs(grid, r, c - 1)
res = 0
r = len(grid)
c = len(grid[0])
for i in range(r):
for j in range(c):
if grid[i][j] == 1:
area = dfs(grid, i, j)
res = max(area, res)
return res
水域大小
解题思路:
这道题与岛屿面积唯一的区别就在于 方向比 岛屿面积多了4个,即上下左右加四个对角,其他思考方式完全相同。
class Solution(object):
def pondSizes(self, land):
"""
:type land: List[List[int]]
:rtype: List[int]
"""
def dfs(land, r, c):
if not(r >= 0 and r < len(land) and c >= 0 and c < len(land[0])):
return 0
if land[r][c] != 0:
return 0
land[r][c] = 1
return 1 + dfs(land, r - 1, c) + dfs(land, r + 1, c) + dfs(land, r, c - 1) + dfs(land, r, c + 1) + \
dfs(land, r - 1, c - 1) + dfs(land, r + 1, c - 1) + dfs(land, r + 1, c + 1) + dfs(land, r - 1, c + 1)
r = len(land)
c = len(land[0])
res = []
for i in range(r):
for j in range(c):
if land[i][j] == 0:
area = dfs(land, i, j)
res.append(area)
res.sort()
return res
岛屿周长
这道题只看代码很难理解!! 请参考力扣
class Solution(object):
def islandPerimeter(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
def dfs(grid, r, l):
if not (r >= 0 and r < len(grid) and l >= 0 and l < len(grid[0])):
return 1
if grid[r][l] == 0:
return 1
if grid[r][l] != 1:
return 0
grid[r][l] = 2
return dfs(grid, r + 1, l) + dfs(grid, r - 1, l) + dfs(grid, r, l + 1) + dfs(grid, r, l - 1)
r = len(grid)
l = len(grid[0])
for i in range(r):
for j in range(l):
if grid[i][j] == 1:
return dfs(grid,i,j)
return 0
岛屿数量
解题思路:
整体代码思路与上面两题一致,不同的地方在于在最后两层循环时,需要记录数量结果。
class Solution(object):
def numIslands(self, grid):
"""
:type grid: List[List[str]]
:rtype: int
"""
def dfs(grid, r, l):
# 仅仅是统计岛屿数量,并不需要返回具体值。
if not (r >= 0 and r < len(grid) and l >= 0 and l < len(grid[0])):
return
if grid[r][l] != '1':
return
grid[r][l] = '2' # 细节的一点是 题目中给出的是字符串,我刚开始调代码,疯狂出错
dfs(grid, r + 1, l)
dfs(grid, r - 1, l)
dfs(grid, r, l - 1)
dfs(grid, r, l + 1)
r = len(grid)
l = len(grid[0])
res = 0
for i in range(r):
for j in range(l):
if grid[i][j] == '1':
res += 1
# 用res统计岛屿数量,同一个岛屿的在一次遍历中都变成了2
dfs(grid, i, j)
return res
颜色填充
这个题是一个标准的岛屿类问题,简直不能再标准了
class Solution(object):
def floodFill(self, image, sr, sc, newColor):
"""
:type image: List[List[int]]
:type sr: int
:type sc: int
:type newColor: int
:rtype: List[List[int]]
"""
def dfs(image, r, l):
if not (r >= 0 and r < len(image) and l >= 0 and l < len(image[0])):
return
if image[r][l] != color:
return
image[r][l] = newColor
dfs(image, r - 1, l)
dfs(image, r + 1, l)
dfs(image, r, l - 1)
dfs(image, r, l + 1)
color = image[sr][sc]
# 如果初始颜色值与新颜色相同直接返回即可
if color == newColor:
return image
dfs(image, sr, sc)
return image
这道题直接套用模板,不用做任何变化
子岛屿
解题思路:
本题在模板的基础上进行了改进:
如果 grid2 的一个岛屿,被 grid1 的一个岛屿 完全 包含,也就是说 grid2 中该岛屿的每一个格子都被 grid1 中同一个岛屿完全包含,那么我们称 grid2 中的这个岛屿为 子岛屿 。
我们需要进行两次深搜,第一次寻找在grid2里面是陆地,而在grid1里面是海洋的点,并把整块陆地变成海洋(这样的陆地是不满足题意中完全包围要求的,即至少有一块没有被完全包含)
第二次深搜就在grid2里面正常深搜就可以,统计岛屿数量就可以得出答案。
class Solution(object):
def countSubIslands(self, grid1, grid2):
"""
:type grid1: List[List[int]]
:type grid2: List[List[int]]
:rtype: int
"""
# 正常的网格深搜模板
def dfs(grid, r, c):
if not(r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0])):
return
if grid[r][c] != 1:
return
grid[r][c] = 0
dfs(grid, r - 1, c)
dfs(grid, r + 1, c)
dfs(grid, r, c - 1)
dfs(grid, r, c + 1)
r = len(grid1)
c = len(grid1[0])
for i in range(r):
for j in range(c):
# 解题最最关键的一部 将grid2中未被完全包含的陆地变成海洋
if grid1[i][j] == 0 and grid2[i][j] == 1:
dfs(grid2, i, j)
# 后面就是统计岛屿数量的代码了 CV一下就可以
res = 0
for i in range(r):
for j in range(c):
if grid2[i][j] == 1:
dfs(grid2, i, j)
res += 1
return res
封闭岛问题
最后两个题目又是新的变种题,即封闭岛屿问题:
封闭岛屿的定义如下:
封闭岛是一个 完全
由1包围(左、上、右、下)的岛。即直接或者间接与网格边界相邻的陆地不算封闭岛。我们在解题前必须了解到这个定义的含义。
解题思路:
那就类似子岛屿问题,先把不是封闭岛的陆地块变成海洋,再统计封闭岛的数量即可。
1.遍历网格的四周,寻找grid = ‘1’ 的陆地,并把陆地变为海洋(0)
r = len(grid)
c = len(grid[0])
for i in range(r):
dfs(grid, i, 0)
dfs(grid, i, c - 1)
for i in range(c):
dfs(grid, 0, i)
dfs(grid, r - 1, i)
2. 统计岛屿数量
整体代码:
class Solution(object):
def closedIsland(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
def dfs(grid, r, c):
if not(r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0])):
return
if grid[r][c] != 0:
return
grid[r][c] = 1
dfs(grid, r - 1, c)
dfs(grid, r + 1, c)
dfs(grid, r, c - 1)
dfs(grid, r, c + 1)
r = len(grid)
c = len(grid[0])
for i in range(r):
dfs(grid, i, 0)
dfs(grid, i, c - 1)
for i in range(c):
dfs(grid, 0, i)
dfs(grid, r - 1, i)
res = 0
for i in range(r):
for j in range(c):
if grid[i][j] == 0:
dfs(grid, i, j)
res += 1
return res
被围绕的区域
解题思路:
和上一个题完全一致,没有任何变动,我也没有感情把他AC掉了
class Solution(object):
def solve(self, board):
"""
:type board: List[List[str]]
:rtype: None Do not return anything, modify board in-place instead.
"""
if not board:
return board
def dfs(board, r, c):
if not (r >= 0 and r < len(board) and c >= 0 and c < len(board[0])):
return
if board[r][c] != 'O':
return
board[r][c] = 'A'
dfs(board, r - 1, c)
dfs(board, r + 1, c)
dfs(board, r, c + 1)
dfs(board, r, c - 1)
r = len(board)
c = len(board[0])
for i in range(r):
dfs(board, i, 0)
dfs(board, i, c - 1)
for i in range(c):
dfs(board, 0, i)
dfs(board, r - 1, i)
for i in range(r):
for j in range(c):
if board[i][j] == 'A':
board[i][j] = "O"
else:
board[i][j] = 'X'
return board
总结:
题解写的确实太少,没有办法把思路讲的特别清楚,也都是按照自己对模板,对题目的理解去写的。想先理解这个问题,可以先参考力扣,大佬的思路解法,“真是妙蛙种子吃着妙脆角妙进了米奇妙妙屋,妙到家了”。