时间:2020-7-12
题目地址:https://leetcode-cn.com/problems/number-of-islands
题目难度:Medium
题目描述:
给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:
[
['1','1','1','1','0'],
['1','1','0','1','0'],
['1','1','0','0','0'],
['0','0','0','0','0']
]
输出: 1
示例 2:
输入:
[
['1','1','0','0','0'],
['1','1','0','0','0'],
['0','0','1','0','0'],
['0','0','0','1','1']
]
输出: 3
解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。
思路1:广度优先遍历
代码段1:一个超时的用例
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
islands_number = 0
visited = []
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == '1' and (i, j) not in visited:
temp = [[i, j]]
print(temp)
visited.append([i, j])
while temp:
x, y = temp[0]
if y > 0:
if grid[x][y - 1] == '1':
if (x, y - 1) not in visited:
temp.append((x, y - 1))
visited.append((x, y - 1))
if y < len(grid[0]) - 1:
if grid[x][y + 1] == '1':
if (x, y + 1) not in visited:
visited.append((x, y + 1))
temp.append((x, y + 1))
if x > 0:
if grid[x - 1][y] == '1':
if (x - 1, y) not in visited:
visited.append((x - 1, y))
temp.append((x - 1, y))
if x < len(grid) - 1:
if grid[x + 1][y] == '1':
if (x + 1, y) not in visited:
visited.append((x + 1, y))
temp.append((x + 1, y))
del temp[0]
print("temp: %s " %temp)
print("visited: %s " %visited)
islands_number += 1
# print(visited)
return islands_number
总结:
- 第一次因为题目没有想的特别清楚,是水平或竖直,我想的是右、下两个位置
- 还有重复的代码,拷贝过来没有修复为新逻辑,改bug也挺有趣的
- 不知道为啥超时,看下liweiwei大佬的代码吧,感觉除了没有方向数字的技巧,使用的列表代替mark数组,其他都一样哎
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 格开始,对每一格尝试进行一次 BFS 操作
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
思路2:深度优先遍历【2020-07-19】
当我们递归地实现 DFS 时,似乎不需要使用任何栈。但实际上,我们使用的是由系统提供的隐式栈,也称为调用栈(Call Stack)。
代码段2:通过
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):
# 只要是陆地,且没有被访问过的,就可以使用 DFS 发现与之相连的陆地,并进行标记
if not marked[i][j] and grid[i][j] == '1':
# count 可以理解为连通分量,你可以在深度优先遍历完成以后,再计数,
# 即这行代码放在【位置 1】也是可以的
count += 1
self.__dfs(grid, i, j, m, n, marked)
# 【位置 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)
总结:
- 呵,递归是我永远的痛
后续优化:并查集