1. 题目
给定一个 m × n m \times n m×n 的二维矩阵 grid
,同时将由横向或纵向按边相接的 1 1 1 (代表陆地)所组成的集合视为岛屿,可以假定矩阵区域之外都为海水。
如果两个岛屿可以且仅可以通过平移的方式(不可进行旋转或镜像)覆盖相同的区域,就称这两个岛屿是相同的,要求返回 grid
中不同岛屿的数量。
1.1 示例
-
示例 1 1 1:
- 输入:
grid = [[1, 1, 0, 0, 0], [1, 1, 0, 0, 0], [0, 0, 0, 1, 1], [0, 0, 0, 1, 1]]
- 输出: 1 1 1
- 输入:
-
示例 2 2 2 :
- 输入:
grid = [[1, 1, 0, 1, 1], [1, 0, 0, 0, 0], [0, 0, 0, 0, 1], [1, 1, 0, 1, 1]]
- 输出: 3 3 3
- 输入:
1.2 说明
- 来源: 力扣(LeetCode)
- 链接: https://leetcode-cn.com/problems/number-of-distinct-islands
1.3 提示
m == grid.length
n == grid[i].length
1 <= m, n <= 50
grid[i][j]
只会是 0 0 0 或者 1 1 1
1.4 进阶
你可以使用多种方法解答这道题目么?(深度优先搜索、广度优先搜索)
2. 解法一(深度优先搜索)
2.1 分析
很显然得想办法把二维矩阵中的「岛屿」进行转化,变成比如字符串这样的类型,然后利用 set
这样的数据结构去重,最终得到不同的岛屿的个数。
如果想把岛屿转化成字符串,说白了就是序列化,序列化说白了就是遍历。
首先,对于形状相同的岛屿,如果从同一起点出发,深度优先遍历的顺序肯定是一样的。因为遍历顺序是写死在递归函数里面的,不会动态改变;所以,遍历顺序从某种意义上说就可以用来描述岛屿的形状,比如下图这两个岛屿:
假设它们的遍历顺序是:
下,右,上,撤销上,撤销右,撤销下
如果我用分别用 1 1 1 , 2 2 2 , 3 3 3 , 4 4 4 代表上下左右,用 − 1 -1 −1 , − 2 -2 −2 , − 3 -3 −3 , − 4 -4 −4 代表上下左右的撤销(这里的撤销可以理解为递归调用的返回),那么可以这样表示它们的遍历顺序:
2 2 2 , 4 4 4 , 1 1 1 , − 1 -1 −1 , − 4 -4 −4 , − 2 -2 −2
最后对不同的遍历顺序进行序列化并去重之后即可得到结果。
2.2 解答
from typing import List
from json import dumps
class Solution:
def _dfs_flood(self, grid: List[List[int]], path: List[int], i: int, j: int, direction: int):
m, n = len(grid), len(grid[0])
if i < 0 or i >= m or j < 0 or j >= n:
return
if grid[i][j] == 0:
return
path.append(direction)
grid[i][j] = 0
self._dfs_flood(grid, path, i - 1, j, 1)
self._dfs_flood(grid, path, i + 1, j, 2)
self._dfs_flood(grid, path, i, j - 1, 3)
self._dfs_flood(grid, path, i, j + 1, 4)
path.append(-direction)
def num_distinct_islands(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
islands = set()
for i in range(m):
for j in range(n):
if grid[i][j] == 1:
path = []
self._dfs_flood(grid, path, i, j, 0)
islands.add(dumps(path))
return len(islands)
def main():
grid = [[1, 1, 0, 0, 0],
[1, 1, 0, 0, 0],
[0, 0, 0, 1, 1],
[0, 0, 0, 1, 1]]
sln = Solution()
print(sln.num_distinct_islands(grid)) # 2
if __name__ == '__main__':
main()
2.3 复杂度
- 时间复杂度: O ( m n ) O(mn) O(mn),其中 m m m 和 n n n 分别为行数和列数。
- 空间复杂度: O ( m n ) O(mn) O(mn) 。