Leetcode刷题笔记62:图论2(广度搜索理论基础-200. 岛屿数量-463. 岛屿的周长)

132 阅读1分钟

导语

leetcode刷题笔记记录,主要记录题目包括:

Leetcode 200. 岛屿数量

题目描述

给你一个由 '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'

解法

上一篇博客使用dfs解决这个问题,这里我们使用bfs来解决这个问题: 当然了,这里是使用广度优先搜索(BFS)解决“岛屿数量”问题的整体思路:

  1. 初始化岛屿数量为零:我们从一个没有发现任何岛屿的状态开始。

  2. 创建一个队列:这个队列将用于BFS过程中,储存我们要检查的网格坐标。

  3. 遍历网格:我们将遍历整个二维网格,寻找陆地(即标记为 '1' 的格子)。

  4. 发现新岛屿时:每当我们遇到一个还未标记(不是 '2')的陆地(即 '1'):

    • 岛屿数量加一。
    • 将该陆地坐标添加到队列中。
    • 标记该陆地为已访问(在这个例子里,我们用 '2' 标记)。
  5. 广度优先搜索(BFS):然后我们执行以下步骤,直到队列为空:

    • 取出队列中的一个坐标(这是一个已经被标记为已访问的陆地)。
    • 查找这个坐标的所有邻居(即上、下、左、右四个方向)。
    • 对于每一个未被访问过的邻居(即标记为 '1'):
      • 标记它为已访问(标记为 '2')。
      • 将它加入队列。
  6. 完成搜索:当队列为空,即意味着我们已经访问了与初始发现的陆地坐标相连的所有陆地,也就是说,我们已经完整地找到了一个岛屿。

  7. 返回结果:遍历完整个二维网格后,返回岛屿数量。

这个算法使用BFS,确保了每一个岛屿只会被计算和访问一次。时间复杂度是 (O(m \times n)),其中 (m) 和 (n) 分别是网格的行数和列数。

from typing import List
from collections import deque

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        # 初始化岛屿数量为 0
        count = 0
        # 获取网格的行数和列数
        m, n = len(grid), len(grid[0])
        
        # 使用 deque(双端队列)来实现 BFS
        queue = deque()
        # 方向数组,用于访问当前点的四个方向
        directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
        
        # 遍历整个二维网格
        for i in range(m):
            for j in range(n):
                # 当找到一个 '1'(陆地)时
                if grid[i][j] == '1':
                    # 岛屿数量加 1
                    count += 1
                    # 标记该陆地为已访问,这里使用 '2'
                    grid[i][j] = '2'
                    # 将该陆地的坐标加入队列
                    queue.append((i, j))
                    
                    # BFS 开始
                    while len(queue) > 0:
                        # 弹出一个点并访问其四周
                        x, y = queue.popleft()
                        
                        # 遍历当前点的四个方向
                        for dx, dy in directions:
                            new_x, new_y = x + dx, y + dy
                            # 如果新点在网格内并且是陆地
                            if 0 <= new_x < m and 0 <= new_y < n and grid[new_x][new_y] == '1':
                                # 标记为已访问
                                grid[new_x][new_y] = '2'
                                # 将新点加入队列,以便进一步搜索
                                queue.append((new_x, new_y))
        
        # 返回岛屿数量
        return count

Leetcode 463. 岛屿的周长

题目描述

给定一个 row x col 的二维网格地图 grid ,其中:grid[i][j] = 1 表示陆地, grid[i][j] = 0 表示水域。

网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。

岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。

 

示例 1:

输入: grid = [[0,1,0,0],[1,1,1,0],[0,1,0,0],[1,1,0,0]]
输出: 16
解释: 它的周长是上面图片中的 16 个黄色的边

示例 2:

输入: grid = [[1]]
输出: 4

示例 3:

输入: grid = [[1,0]]
输出: 4

 

提示:

  • row == grid.length
  • col == grid[i].length
  • 1 <= row, col <= 100
  • grid[i][j] 为 0 或 1

解法

直接遍历格子,如果它是陆地,则周长加上4,但同时,如果它的左边是陆地,那么会有一条公共边,周长减去2;同理如果它的上面是陆地,也要减去2。

完整代码如下:

class Solution:
    def islandPerimeter(self, grid: List[List[int]]) -> int:
        ans = 0
        m, n = len(grid), len(grid[0])

        for i in range(m):
            for j in range(n):
                if grid[i][j] == 1:
                    ans += 4
                    if i-1>=0 and grid[i-1][j]==1:
                        ans -= 2
                    if j-1>=0 and grid[i][j-1]==1:
                        ans -= 2
        return ans