Python算法题集_岛屿数量
- 题200:岛屿数量
- 1. 示例说明
- 2. 题目解析
- - 题意分解
- - 优化思路
- - 测量工具
- 3. 代码展开
- 1) 标准求解【双层循环+递归】
- 2) 改进版一【双层循环+迭代】
- 3) 改进版二【双层循环+迭代+高速双向队列】
- 4) 改进版三【双层循环+并查集】
- 4. 最优算法
- 5. 相关资源
本文为Python算法题集之一的代码示例
题200:岛屿数量
1. 示例说明
-
给你一个由
'1'
(陆地)和'0'
(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:grid = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ] 输出:1
示例 2:
输入:grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"] ] 输出:3
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j]
的值为'0'
或'1'
2. 题目解析
- 题意分解
- 本题是在图中计算岛屿数量,“1”为陆地,"0"为水面
- 本题必须通过陆地着手计算,因为即使只有一块水域,也可以有多块陆地
- 基本的设计思路是双层循环,自上而下,从左到右,遍历网格;每发现一块陆地,就将相连的陆地全部找出来标记为已检查
- 优化思路
-
通常优化:减少循环层次
-
通常优化:增加分支,减少计算集
-
通常优化:采用内置算法来提升计算速度
-
分析题目特点,分析最优解
-
可以采用递归法进行相连陆地的遍历
-
可以采用迭代法进行相连陆地的遍历,迭代法的队列如果使用pop(0),则可以考虑使用高速双向队列
deque
来优化 -
可以采用并查集法,将一块岛屿的所有陆地合并到1个类别中,最后检查有多少种类别
-
- 测量工具
- 本地化测试说明:LeetCode网站测试运行时数据波动很大【可把页面视为功能测试】,因此需要本地化测试解决数据波动问题
CheckFuncPerf
(本地化函数用时和内存占用测试模块)已上传到CSDN,地址:Python算法题集_检测函数用时和内存占用的模块- 本题本地化超时测试用例自己生成,详见【最优算法章节】
3. 代码展开
1) 标准求解【双层循环+递归】
双层循环,使用递归法遍历相连陆地
页面功能测试,马马虎虎,超过67%
import CheckFuncPerf as cfp
class Solution:
def numIslands_base(self, grid):
directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]
def dfs(grid, irow, icol):
if not 0 <= irow < len(grid) or not 0 <= icol < len(grid[0]):
return 0
if grid[irow][icol]!= "1":
return 0
grid[irow][icol]= "2"
for delta in directions:
dfs(grid, irow+delta[0], icol+delta[1])
return 1
landnum=0
for iIdx in range(len(grid)):
for jIdx in range(len(grid[0])):
if grid[iIdx][jIdx]=="1":
landnum+=dfs(grid,iIdx,jIdx)
return landnum
aSolution = Solution()
list_world = copy.deepcopy(list_originworld)
result = cfp.getTimeMemoryStr(Solution.numIslands_base, aSolution, list_world)
print(result['msg'], '执行结果 = {}'.format(result['result']))
# 运行结果
函数 numIslands_base 的运行时间为 604.38 ms;内存使用量为 168.00 KB 执行结果 = 66293
2) 改进版一【双层循环+迭代】
双层循环,使用迭代法遍历相连陆地
页面功能测试,性能良好,超过84%
import CheckFuncPerf as cfp
class Solution:
def numIslands_ext1(self, grid):
queue = []
directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]
imaxrow = len(grid)
imaxcol = len(grid[0])
bvisited = [[False] * imaxcol for x in range(imaxrow)]
landnum = 0
for iIdx in range(imaxrow):
for jIdx in range(imaxcol):
if bvisited[iIdx][jIdx] or grid[iIdx][jIdx] == '0':
continue
bvisited[iIdx][jIdx] = True
queue.append([iIdx, jIdx])
while queue:
irow, icol = queue[0][0], queue[0][1]
queue.pop(0)
for delta in directions:
inextrow, inextcol = irow + delta[0], icol + delta[1]
if inextrow < 0 or inextrow >= imaxrow or inextcol < 0 \
or inextcol >= imaxcol or bvisited[inextrow][inextcol] \
or grid[inextrow][inextcol] == '0':
continue
queue.append([inextrow, inextcol])
bvisited[inextrow][inextcol] = True
landnum += 1
return landnum
aSolution = Solution()
list_world = copy.deepcopy(list_originworld)
result = cfp.getTimeMemoryStr(Solution.numIslands_ext1, aSolution, list_world)
print(result['msg'], '执行结果 = {}'.format(result['result']))
# 运行结果
函数 numIslands_ext1 的运行时间为 508.67 ms;内存使用量为 840.00 KB 执行结果 = 66293
3) 改进版二【双层循环+迭代+高速双向队列】
双层循环,使用迭代法遍历相连陆地,并使用高速双向队列deque
来优化list的pop(0)性能
页面功能测试,性能优越,超越92%
import CheckFuncPerf as cfp
class Solution:
def numIslands_ext2(self, grid):
from collections import deque
queue = deque([])
directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]
imaxrow = len(grid)
imaxcol = len(grid[0])
bvisited = [[False] * imaxcol for x in range(imaxrow)]
landnum = 0
for iIdx in range(imaxrow):
for jIdx in range(imaxcol):
if bvisited[iIdx][jIdx] or grid[iIdx][jIdx] == '0':
continue
bvisited[iIdx][jIdx] = True
queue.append([iIdx, jIdx])
while queue:
irow, icol = queue[0][0], queue[0][1]
queue.popleft()
for delta in directions:
inextrow, inextcol = irow + delta[0], icol + delta[1]
if inextrow < 0 or inextrow >= imaxrow or inextcol < 0 \
or inextcol >= imaxcol or bvisited[inextrow][inextcol] \
or grid[inextrow][inextcol] == '0':
continue
queue.append([inextrow, inextcol])
bvisited[inextrow][inextcol] = True
landnum += 1
return landnum
aSolution = Solution()
list_world = copy.deepcopy(list_originworld)
result = cfp.getTimeMemoryStr(Solution.numIslands_ext2, aSolution, list_world)
print(result['msg'], '执行结果 = {}'.format(result['result']))
# 运行结果
函数 numIslands_ext2 的运行时间为 473.73 ms;内存使用量为 24.00 KB 执行结果 = 66293
4) 改进版三【双层循环+并查集】
双层循环,使用并查集【通过字典实现find
、union
】合并陆地,最后检查类别数
页面功能测试,惨不忍睹,超过33%
import CheckFuncPerf as cfp
class Solution:
def numIslands_ext3(self, grid):
dict_type = {}
def find(x):
dict_type.setdefault(x, x)
if dict_type[x] != x:
dict_type[x] = find(dict_type[x])
return dict_type[x]
def union(x, y):
dict_type[find(x)] = find(y)
if not grid:
return 0
imaxrow = len(grid)
imaxcol = len(grid[0])
directions = [[-1, 0], [0, -1]]
for iIdx in range(imaxrow):
for jIdx in range(imaxcol):
if grid[iIdx][jIdx] == "1":
for delta in directions:
tmprow_i = iIdx + delta[0]
tmpcol_j = jIdx + delta[1]
if 0 <= tmprow_i < imaxrow and 0 <= tmpcol_j < imaxcol and grid[tmprow_i][tmpcol_j] == "1":
union(tmprow_i * imaxrow + tmpcol_j, iIdx * imaxrow + jIdx)
lands = set()
for iIdx in range(imaxrow):
for jIdx in range(imaxcol):
if grid[iIdx][jIdx] == "1":
tmpType = find(iIdx * imaxrow + jIdx)
if tmpType in lands: continue
lands.add(tmpType)
return len(lands)
aSolution = Solution()
list_world = copy.deepcopy(list_originworld)
result = cfp.getTimeMemoryStr(Solution.numIslands_ext3, aSolution, list_world)
print(result['msg'], '执行结果 = {}'.format(result['result']))
# 运行结果
函数 numIslands_ext2 的运行时间为 473.73 ms;内存使用量为 24.00 KB 执行结果 = 66293
4. 最优算法
根据本地日志分析,最优算法为第3种方式【双层循环+迭代+高速双向队列】numIslands_ext2
import random, copy
imaxrow, imaxcol = 1000, 1000
list_originworld = [[str(random.randint(0, 1)) for y in range(imaxcol)] for x in range(imaxrow)]
aSolution = Solution()
list_world = copy.deepcopy(list_originworld)
result = cfp.getTimeMemoryStr(Solution.numIslands_base, aSolution, list_world)
print(result['msg'], '执行结果 = {}'.format(result['result']))
list_world = copy.deepcopy(list_originworld)
result = cfp.getTimeMemoryStr(Solution.numIslands_ext1, aSolution, list_world)
print(result['msg'], '执行结果 = {}'.format(result['result']))
list_world = copy.deepcopy(list_originworld)
result = cfp.getTimeMemoryStr(Solution.numIslands_ext2, aSolution, list_world)
print(result['msg'], '执行结果 = {}'.format(result['result']))
list_world = copy.deepcopy(list_originworld)
result = cfp.getTimeMemoryStr(Solution.numIslands_ext3, aSolution, list_world)
print(result['msg'], '执行结果 = {}'.format(result['result']))
# 算法本地速度实测比较
函数 numIslands_base 的运行时间为 604.38 ms;内存使用量为 168.00 KB 执行结果 = 66293
函数 numIslands_ext1 的运行时间为 508.67 ms;内存使用量为 840.00 KB 执行结果 = 66293
函数 numIslands_ext2 的运行时间为 473.73 ms;内存使用量为 24.00 KB 执行结果 = 66293
函数 numIslands_ext3 的运行时间为 919.58 ms;内存使用量为 36804.00 KB 执行结果 = 66293
5. 相关资源
本文代码已上传到CSDN,地址:Python算法题集_LeetCode(力扣)_岛屿数量_源代码
一日练,一日功,一日不练十日空
may the odds be ever in your favor ~