Python算法题集_岛屿数量

本文为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. 本题是在图中计算岛屿数量,“1”为陆地,"0"为水面
  2. 本题必须通过陆地着手计算,因为即使只有一块水域,也可以有多块陆地
  3. 基本的设计思路是双层循环,自上而下,从左到右,遍历网格;每发现一块陆地,就将相连的陆地全部找出来标记为已检查

- 优化思路

  1. 通常优化:减少循环层次

  2. 通常优化:增加分支,减少计算集

  3. 通常优化:采用内置算法来提升计算速度

  4. 分析题目特点,分析最优解

    1. 可以采用递归法进行相连陆地的遍历

    2. 可以采用迭代法进行相连陆地的遍历,迭代法的队列如果使用pop(0),则可以考虑使用高速双向队列deque来优化

    3. 可以采用并查集法,将一块岛屿的所有陆地合并到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) 改进版三【双层循环+并查集】

双层循环,使用并查集【通过字典实现findunion】合并陆地,最后检查类别数

页面功能测试,惨不忍睹,超过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 ~

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长孤秋落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值