网格问题中的DFS
网格问题
由 m*n 个小方格组成一个网格,每个小方格与其 「上下左右」 四个方格认为是相邻的,要在这样的网格上进行某种搜索。
岛屿问题是网格DFS问题的典型代表。
网格结构中的DFS
二叉树的 DFS 有两个要素:「访问相邻结点」和「判断 base case」
- 访问相邻结点:即递归调用左右子节点
- 判断base case:root == None
因此,网格上的DFS
- 相邻结点:上下左右四个
- base case:下标越界,超出网格范围的格子
网格结构的 DFS 与二叉树的 DFS 最大的不同之处在于,遍历中可能遇到遍历过的结点,标记访问过的位置。
岛屿问题解法
695. 岛屿的最大面积(中等)
class Solution:
def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
if not grid:
return 0
m = len(grid)
n = len(grid[0])
res = 0
visited = [[0 for _ in range(n)] for _ in range(m)]
steps = [(0,1),(0,-1),(1,0),(-1,0)]
def inArea(i,j): #判断是否在网格内
if i>=0 and i<m and j>=0 and j<n and grid[i][j]==1 and visited[i][j]==0:
return True
return False
def dfs(i,j):#返回(i,j)出发的岛屿面积
a = 1
for step in steps:
x = i+step[0]
y = j+step[1]
if inArea(x,y):
visited[x][y] = 1
a += dfs(x,y)
return a
for i in range(m):
for j in range(n):
if grid[i][j]==1 and visited[i][j]==0:
visited[i][j] = 1
res = max(res,dfs(i,j))
return res
827. 最大人工岛(困难)
方法一: 暴力DFS,超时
以每格海洋为源进行深度优先遍历,area=它上下左右所有岛屿面积之和+1
class Solution:
def largestIsland(self, grid: List[List[int]]) -> int:
if not grid:
return 0
m = len(grid)
n = len(grid[0])
res = 0
steps = [(0,1),(0,-1),(1,0),(-1,0)]
def dfs(i,j,visited):
a = 1
for step in steps:
x = i+step[0]
y = j+step[1]
if x>=0 and x<m and y>=0 and y<n and grid[x][y]==1 and visited[x][y]==0:
visited[x][y] = 1
a += dfs(x,y,visited)
return a
flag = False
for i in range(m):
for j in range(n):
if grid[i][j] == 0:
flag = True
visited = [[0 for _ in range(n)] for _ in range(m)]
res = max(res,dfs(i,j,visited))
if flag:
return res
return m*n
方法二:
在方法一中重复计算了连通块
先计算出所有岛屿的面积,搜索哪个海洋格子相邻的岛屿面积最大,注意区分上下左右相邻的是否是同一岛屿(如下图所示)。
思路:
第一次遍历,计算每个连通块的面积,分别赋成index,并用字典保存对应的面积;
第二次遍历,计算每个海洋格子周围的岛屿面积。
class Solution:
def largestIsland(self, grid: List[List[int]]) -> int:
if not grid:
return 0
m = len(grid)
n = len(grid[0])
res = 0
steps = [(0,1),(0,-1),(1,0),(-1,0)]
def dfs(i,j,idx):
a = 1
for step in steps:
x = i+step[0]
y = j+step[1]
if x>=0 and x<m and y>=0 and y<n and grid[x][y]==1:
grid[x][y] = idx
a += dfs(x,y,idx)
return a
dic = {}
idx = 1
flag = False
for i in range(m):
for j in range(n):
if grid[i][j] == 1:
idx += 1
grid[i][j] = idx
dic[idx] = dfs(i,j,idx)
res = 0
for i in range(m):
for j in range(n):
if grid[i][j]==0:
area = 1
flag = True
seen = set()
for step in steps:
x = i+step[0]
y = j+step[1]
if x>=0 and x<m and y>=0 and y<n and grid[x][y]!=0:
key = grid[x][y]
if key not in seen:
area += dic[key]
seen.add(key)
res = max(res,area)
if flag:
return res
return m*n
463. 岛屿的周长(简单)
dfs函数直接返回有以下几种情况:
- 坐标不在网格内(周长+1)
- 是海洋格子(周长+1)
- 是已遍历过的陆地格子
class Solution:
def islandPerimeter(self, grid: List[List[int]]) -> int:
if not grid:
return 0
m = len(grid)
n = len(grid[0])
steps = [(0,1),(0,-1),(1,0),(-1,0)]
def dfs(i,j):
l = 0
for step in steps:
x = i+step[0]
y = j+step[1]
if x<0 or x>=m or y<0 or y>=n:
l += 1
elif x>=0 and x<m and y>=0 or y<n:
if grid[x][y] == 1:
grid[x][y] = 2
l += dfs(x,y)
elif grid[x][y] == 0:
l += 1
return l
res = 0
for i in range(m):
for j in range(n):
if grid[i][j]==1:
grid[i][j] = 2
return dfs(i,j)
class Solution:
def islandPerimeter(self, grid: List[List[int]]) -> int:
if not grid:
return 0
m = len(grid)
n = len(grid[0])
steps = [(0,1),(0,-1),(1,0),(-1,0)]
#先污染后治理,判断当前的格子
def dfs(i,j):
if i<0 or i>=m or j<0 or j>=n:
return 1
if grid[i][j] == 0:
return 1
if grid[i][j] != 1:
return 0
grid[i][j] = 2
l = 0
for step in steps:
x = i+step[0]
y = j+step[1]
l += dfs(x,y)
return l
res = 0
for i in range(m):
for j in range(n):
if grid[i][j]==1:
return dfs(i,j)
参考资料:
https://mp.weixin.qq.com/s?__biz=MzA5ODk3ODA4OQ==&mid=2648167208&idx=1&sn=d8118c7c0e0f57ea2bdd8aa4d6ac7ab7&chksm=88aa236ebfddaa78a6183cf6dcf88f82c5ff5efb7f5c55d6844d9104b307862869eb9032bd1f&token=1064083695&lang=zh_CN#rd