• leetcode&lintcode分类刷题:图论(一、连通域/岛屿问题)


    1、本次总结的题目通常是在二维矩阵考察连通域/岛屿问题,常用的方法包括深度优先搜索、广度优先搜索和并查集,根据具体的题目可以选择最合适的方法,我个人优选在逻辑思维上简单直观的广度优先搜索方法
    2、二维矩阵考察连通域/岛屿问题,包括简单的连通域染色、岛屿数量、飞地数量、岛屿面积等,复杂一点的题目考察对每个连通域/岛屿如何更好地标记,比如最大人工岛(简单的数字标记)、岛屿形状(相对位置连起来的元组或字符串标记)等

    733. 图像渲染

    本题是连通域染色问题的基础题目,是深度优先搜索或广度优先搜索的基本练习题,需要注意不用染色的特殊情况,比较细节

    from typing import List
    from collections import deque
    '''
    733. 图像渲染
    有一幅以m x n的二维整数数组表示的图画image,其中image[i][j]表示该图画的像素值大小。
    你也被给予三个整数 sr , sc 和 newColor 。你应该从像素image[sr][sc]开始对图像进行 上色填充 。
    为了完成 上色工作 ,从初始像素开始,记录初始坐标的 上下左右四个方向上 像素值与初始坐标相同的相连像素点,
    接着再记录这四个方向上符合条件的像素点与他们对应 四个方向上 像素值与初始坐标相同的相连像素点,……,重复该过程。
    将所有有记录的像素点的颜色值改为newColor。
    最后返回 经过上色渲染后的图像。
    示例 1:
        输入: image = [[1,1,1],[1,1,0],[1,0,1]],sr = 1, sc = 1, newColor = 2
        输出: [[2,2,2],[2,2,0],[2,0,1]]
        解析: 在图像的正中间,(坐标(sr,sc)=(1,1)),在路径上所有符合条件的像素点的颜色都被更改成2。
        注意,右下角的像素没有更改为2,因为它不是在上下左右四个方向上与初始点相连的像素点。
    题眼:连通域染色问题,DFS、BFS的基础操作
    '''
    
    
    class Solution:
        def floodFill(self, image: List[List[int]], sr: int, sc: int, color: int) -> List[List[int]]:
            # 思路1、深度优先搜索
            # 情况1、矩阵为空
            if not image:
                return image
            m, n = len(image), len(image[0])
            val = image[sr][sc]
            # 情况2、矩阵不用染色
            if val == color:
                return image
            # 情况3、深度优先搜索
            # 1、定义递归函数
            def dfs(x, y):
                # 2、终止条件
                if not 0 <= x < m or not 0 <= y < n or image[x][y] != val:  # 不合法&非连通 直接返回
                    return
                # 3、递归操作
                image[x][y] = color
                dfs(x+1, y)
                dfs(x-1, y)
                dfs(x, y+1)
                dfs(x, y-1)
            dfs(sr, sc)
            return image
    
            # # 思路2、广度优先搜索
            # # 情况1、矩阵为空
            # if not image:
            #     return image
            # m, n = len(image), len(image[0])
            # val = image[sr][sc]
            # # 情况2、矩阵不用染色
            # if val == color:
            #     return image
            # 情况3、广度优先搜索
            # que = deque()
            # que.append((sr, sc))
            # image[sr][sc] = color  # 入队立刻染色
            # while que:
            #     x, y = que.popleft()
            #     for tx, ty in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):  # 沿 4个方向 搜索
            #         if 0 <= tx < m and 0 <= ty < n and image[tx][ty] == val:  # 合法&连通 才入队
            #             que.append((tx, ty))
            #             image[tx][ty] = color  # 入队立刻染色
            # return image
    
    
    if __name__ == "__main__":
        obj = Solution()
        while True:
            try:
                in_line = input().strip().split('=')
                in_line1 = in_line[1].strip().split('s')[0].strip()[1: -2]
                image = []
                for row in in_line1.split(']')[: -1]:
                    image.append([int(i) for i in row.split('[')[1].split(',')])
                sr = int(in_line[2].strip().split(',')[0])
                sc = int(in_line[3].strip().split(',')[0])
                newColor = int(in_line[4].strip())
                print(obj.floodFill(image, sr, sc, newColor))
            except EOFError:
                break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    1034. 边界着色

    ”733. 图像渲染“的扩展,理解题很重要:只对连通域的边界位置的方块着色,可以把边界方块放到一个列表中,遍历完矩阵后再染色

    from typing import List
    from collections import deque
    '''
    1034. 边界着色
    给你一个大小为 m x n 的整数矩阵 grid ,表示一个网格。另给你三个整数row、col 和 color 。网格中的每个值表示该位置处的网格块的颜色。
    两个网格块属于同一 连通分量 需满足下述全部条件:
        两个网格块颜色相同
        在上、下、左、右任意一个方向上相邻
    连通分量的边界 是指连通分量中满足下述条件之一的所有网格块:
        在上、下、左、右任意一个方向上与不属于同一连通分量的网格块相邻
        在网格的边界上(第一行/列或最后一行/列)
    请你使用指定颜色color 为所有包含网格块grid[row][col] 的 连通分量的边界 进行着色,并返回最终的网格grid 。
    示例 1:
        输入:grid = [[1,1],[1,2]], row = 0, col = 0, color = 3
        输出:[[3,3],[3,2]]
    题眼:”733. 图像渲染“的扩展,理解题很重要:只对连通域的边界方块着色,可以把边界方块放到一个列表中
    思路:第一步,找到 “连通区域”的边界方块,并把它保存起来
         第二步,对保存的边界方块进行着色
    细节:如果color==grid[row][col],则不必着色,更不必寻找 “连通区域”的边界
    '''
    
    
    class Solution:
        def colorBorder(self, grid: List[List[int]], row: int, col: int, color: int) -> List[List[int]]:
            # 理解题很重要:只对边界区域着色,可以把边界位置放到一个列表中
            # 思路1、广度优先遍历
            # # 情况1、矩阵为空
            # if not grid:
            #     return []
            # # 情况2、不需要着色
            # if grid[row][col] == color:
            #     return grid
            # # 情况3、需要着色
            # que = deque()
            # m, n = len(grid), len(grid[0])
            # visited = [[False] * n for _ in range(m)]
            # que.append((row, col))
            # visited[row][col] = True  # 入队就标记
            # target = grid[row][col]
            # border = []
            # while len(que) > 0:
            #     x, y = que.popleft()
            #     # 对连通域的每个块判断是否为边界
            #     if x == 0 or x == m-1 or y == 0 or y == n-1:
            #         border.append((x, y))
            #     elif grid[x+1][y] != target or grid[x-1][y] != target or grid[x][y+1] != target or grid[x][y-1] != target:
            #         border.append((x, y))
            #     for tx, ty in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
            #         if 0 <= tx < m and 0 <= ty < n and not visited[tx][ty] and grid[tx][ty] == target:
            #             que.append((tx, ty))
            #             visited[tx][ty] = True
            # for i, j in border:
            #     grid[i][j] = color
            # return grid
    
    
            # 思路2、深度优先遍历
            # 情况1、矩阵为空
            if not grid:
                return []
            # 情况2、不需要着色
            if grid[row][col] == color:
                return grid
            # 情况3、需要着色
            m, n = len(grid), len(grid[0])
            visited = [[False] * n for _ in range(m)]
            target = grid[row][col]
            border = []
            # 1、定义递归函数
            def dfs(x: int, y: int):
                # 2、终止条件
                if not 0 <= x < m or not 0 <= y < n or visited[x][y] or grid[x][y] != target:
                    return
                # 3、递归操作
                visited[x][y] = True
                if x == 0 or x == m-1 or y == 0 or y == n-1:
                    border.append((x, y))
                elif grid[x+1][y] != target or grid[x-1][y] != target or grid[x][y+1] != target or grid[x][y-1] != target:
                    border.append(((x, y)))
                dfs(x+1, y)
                dfs(x-1, y)
                dfs(x, y+1)
                dfs(x, y-1)
            dfs(row, col)
            for i, j in border:
                grid[i][j] = color
            return grid
    
    
    if __name__ == "__main__":
        obj = Solution()
        while True:
            try:
                in_line = input().strip().split('=')
                grid = []
                if in_line[1].strip().split('r')[0].strip()[1: -2] != '':
                    for row in in_line[1].strip().split('r')[0].strip()[1: -2].split(']')[:-1]:
                        grid.append([int(n) for n in row.split('[')[1].split(',')])
                row = int(in_line[2].split(',')[0])
                col = int(in_line[3].split(',')[0])
                color = int(in_line[4])
                print(obj.colorBorder(grid, row, col, color))
            except EOFError:
                break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104

    200. 岛屿数量

    1、岛屿数量问题的基础题目,也是考察连通域的个数,不像前面两道题一样给定起始的搜索位置了,需要对二维矩阵从首位置遍历到末位置进行搜索了,推荐简单直观的广度优先搜索方法
    2、注意细节就是入队的位置及时标记,否则可能会超时

    from typing import List, Optional, Union
    from collections import deque
    '''
    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
    模板:广搜或深搜都可以解决;只要能把相邻且相同属性的节点标记上就行。
    题眼:连通域个数问题,DFS、BFS的基础操作
    思路:采用visited矩阵标记访问过的位置,可以避免原矩阵被修改!
    '''
    
    
    class Solution:
        def numIslands(self, grid: List[List[str]]) -> int:
            # 思路1、广度优先搜索
            # 特殊情况:区域为空
            if not grid:
                return 0
            result = 0
            m, n = len(grid), len(grid[0])
            visited = [[False] * n for _ in range(m)]  # 标记矩阵
            que = deque()
            for i in range(m):
                for j in range(n):
                    if grid[i][j] == '1' and not visited[i][j]:
                        que.append((i, j))
                        visited[i][j] = True  # 注意:必须立刻标记,否则会在后续有太多重复判断,会超时
                        while len(que) > 0:
                            x, y = que.popleft()
                            for tx, ty in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
                                if 0 <= tx < m and 0 <= ty < n and not visited[tx][ty] and grid[tx][ty] == '1':
                                    que.append((tx, ty))
                                    visited[tx][ty] = True
                        result += 1
            return result
            # # 思路2、深度优先搜索
            # # 特殊情况:区域为空
            # if not grid:
            #     return 0
            # result = 0
            # m, n = len(grid), len(grid[0])
            # visited = [[False] * n for _ in range(m)]  # 标记矩阵
            # # 1、定义递归函数
            # def dfs(x: int, y: int):
            #     # 2、终止条件
            #     if not 0 <= x < m or not 0 <= y < n or visited[x][y] or grid[x][y] != '1':
            #         return
            #     # 3、递归操作
            #     visited[x][y] = True
            #     dfs(x+1, y)
            #     dfs(x-1, y)
            #     dfs(x, y+1)
            #     dfs(x, y-1)
            # for i in range(m):
            #     for j in range(n):
            #         if grid[i][j] == '1' and not visited[i][j]:  # 注意:不可以在调用前进行标记处理,这样就无法实现dfs()递归
            #             dfs(i, j)
            #             result += 1
            # return result
    
    
    if __name__ == "__main__":
        obj = Solution()
        while True:
            try:
                in_line = input().strip().split('=')[1].strip()[1: -1]
                grid = []
                for row in in_line.split(']')[: -1]:
                    ans = []
                    for s in row.split('[')[1].split(','):
                        ans.append(s[1: -1])
                    grid.append(ans)
                obj.numIslands(grid)
                print(grid)
    
            except EOFError:
                break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    1020. 飞地的数量

    ”200. 岛屿数量“的扩展,正常广度优先搜索方法基础上,额外添加 连通域内方块个数统计及连通域是否为飞地的判断即可——只需要一次遍历即可

    from typing import List, Optional, Union
    from collections import deque
    '''
    1020. 飞地的数量
    给你一个大小为 m x n 的二进制矩阵 grid ,其中 0 表示一个海洋单元格、1 表示一个陆地单元格。
    一次 移动 是指从一个陆地单元格走到另一个相邻(上、下、左、右)的陆地单元格或跨过 grid 的边界。
    返回网格中 无法 在任意次数的移动中离开网格边界的陆地单元格的数量
    示例 1:
        输入:grid = [[0,0,0,0],[1,0,1,0],[0,1,1,0],[0,0,0,0]]
        输出:3
        解释:有三个 1 被 0 包围。一个 1 没有被包围,因为它在边界上。
    题眼:”200. 岛屿数量“的扩展,正常BFS遍历添加上 连通域内方块个数及连通域是否为飞地的判断即可——只需要一次遍历即可
    按照这个思路改成DFS时,改不出来,我太菜了!
    '''
    
    
    class Solution:
        def numEnclaves(self, grid: List[List[int]]) -> int:
            # 思路1、广度优先搜索
            # 情况1、矩阵为空
            if not grid:
                return 0
            # 情况2、矩阵不为空
            que = deque()
            m, n = len(grid), len(grid[0])
            visited = [[False] * n for _ in range(m)]
            result = 0
            for i in range(m):
                for j in range(n):
                    if grid[i][j] == 1 and not visited[i][j]:
                        que.append((i, j))
                        visited[i][j] = True
                        temp_num = 1  # 记录连通域内单元格数量
                        isEnclave = True  # 标记连通域是否为飞地
                        while len(que) > 0:
                            x, y = que.popleft()
                            if isEnclave:
                                if x == 0 or x == m-1 or y == 0 or y == n-1:
                                    isEnclave = False
                            for tx, ty in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
                                if 0 <= tx < m and 0 <= ty < n and not visited[tx][ty] and grid[tx][ty] == 1:
                                    que.append((tx, ty))
                                    visited[tx][ty] = True
                                    temp_num += 1
                        if isEnclave:
                            result += temp_num
            return result
    
    
    if __name__ == "__main__":
        obj = Solution()
        while True:
            try:
                in_line = input().strip().split('=')[1].strip()[1: -1]
                grid = []
                for row in in_line.split(']')[: -1]:
                    grid.append([int(i) for i in row.split('[')[1].split(',')])
                print(obj.numEnclaves(grid))
            except EOFError:
                break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    1254. 统计封闭岛屿的数目

    “1020. 飞地的数量”一样的题意,正常广度优先搜索方法基础上,额外添加 连通域是否为飞地的判断即可

    from typing import List, Optional, Union
    from collections import deque
    '''
    1254. 统计封闭岛屿的数目
    二维矩阵 grid由 0(土地)和 1(水)组成。岛是由最大的4个方向连通的 0组成的群,封闭岛是一个完全 由1包围(左、上、右、下)的岛。
    请返回 封闭岛屿 的数目。
    示例 1:
        输入:grid = [[1,1,1,1,1,1,1,0],[1,0,0,0,0,1,1,0],[1,0,1,0,1,1,1,0],[1,0,0,0,0,1,0,1],[1,1,1,1,1,1,1,0]]
        输出:2
        解释:
        灰色区域的岛屿是封闭岛屿,因为这座岛屿完全被水域包围(即被 1 区域包围)。
    题眼:和“1020. 飞地的数量”是完全一样的
    思路:第一步、正常访问并跟踪grid的岛屿
         第二步、判断跟踪的岛屿是否是 封闭的:用一个标记去跟踪遍历的岛屿(只要连通域没有在边界上即可),但是一定要把整个岛屿遍历完,不能加break提前跳出
    '''
    
    
    class Solution:
        def closedIsland(self, grid: List[List[int]]) -> int:
            # 这道题目就是飞地的数量
            result = 0
            que = deque()
            m, n = len(grid), len(grid[0])
            visited = [[False] * n for _ in range(m)]
            for i in range(m):
                for j in range(n):
                    if grid[i][j] == 0 and not visited[i][j]:
                        que.append((i, j))
                        visited[i][j] = True
                        isClose = True
                        while len(que) > 0:
                            x, y = que.popleft()
                            if isClose:
                                if x == 0 or x == m-1 or y == 0 or y == n-1:
                                    isClose = False
                            for tx, ty in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
                                if 0 <= tx < m and 0 <= ty < n and not visited[tx][ty] and grid[tx][ty] == 0:
                                    que.append((tx, ty))
                                    visited[tx][ty] = True
                        if isClose:
                            result += 1
            return result
    
    
    if __name__ == "__main__":
        obj = Solution()
        while True:
            try:
                in_line = input().strip().split('=')
                grid = []
                for row in in_line[1].strip()[1: -1].split(']')[: -1]:
                    grid.append([int(n) for n in row.split('[')[1].split(',')])
                print(obj.closedIsland(grid))
            except EOFError:
                break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    130. 被围绕的区域

    “1020. 飞地的数量”一样的题意,正常广度优先搜索方法基础上,额外添加 连通域内方块都预存及连通域是否为飞地的判断即可——只需要一次遍历即可

    import collections
    from typing import List, Optional, Union
    from collections import deque
    '''
    130. 被围绕的区域
    给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。
    示例 1:
        输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]
        输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]]
        解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的'O'都不会被填充为'X'。 任何不在边界上,或不与边界上的'O'相连的'O'最终都会被填充为'X'。
        如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
    题眼:“1020. 飞地的数量”一样的题意,正常BFS遍历,添加上 连通域内方块都预存及连通域是否为飞地的判断即可——只需要一次遍历即可
    按照这个思路改成DFS时,还是改不出来,我太菜了!
    '''
    
    
    class Solution:
        def solve(self, board: List[List[str]]) -> None:
            # 跟“飞地的数量”是一样的题意
            # 用擅长的广度优先算法做吧
            # 情况1、矩阵为空
            if not board:
                return
            # 情况2、矩阵不为空
            que = deque()
            m, n = len(board), len(board[0])
            visited = [[False] * n for _ in range(m)]
            result = []
            for i in range(m):
                for j in range(n):
                    if board[i][j] == 'O' and not visited[i][j]:
                        que.append((i, j))
                        visited[i][j] = True
                        temp = []  # 将连通域内的所有节点都记录下来
                        isAdd = True  # 默认连通域是飞地
                        while len(que) > 0:
                            x, y = que.popleft()
                            temp.append((x, y))
                            if isAdd:
                                if x == 0 or x == m-1 or y == 0 or y == n-1:
                                    isAdd = False
                            for tx, ty in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
                                if 0 <= tx < m and 0 <= ty < n and not visited[tx][ty] and board[tx][ty] == 'O':
                                    que.append((tx, ty))
                                    visited[tx][ty] = True
                        if isAdd:
                            result.append(temp)
            for temp in result:
                for x, y in temp:
                    board[x][y] = 'X'
    
    
    if __name__ == "__main__":
        obj = Solution()
        while True:
            try:
                in_line = input().strip().split('=')[1].strip()[1: -1]
                board = []
                for row in in_line.split(']')[: -1]:
                    ans = []
                    for s in row.split('[')[1].split(','):
                        ans.append(s[1: -1])
                    board.append(ans)
                obj.solve(board)
                print(board)
    
            except EOFError:
                break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    1905. 统计子岛屿

    有点类似于飞地(“1020. 飞地的数量”)的判断,读懂题很重要:grid2的岛屿,对应到grid1位置都为1的话,说明就是个grid1的子岛屿,正常广度优先搜索方法基础上,额外添加 对应位置到grid1中是否也都为1的判断即可

    from typing import List, Optional, Union
    from collections import deque
    '''
    1905. 统计子岛屿
    给你两个m x n的二进制矩阵grid1 和grid2,它们只包含0(表示水域)和 1(表示陆地)。一个 岛屿是由 四个方向(水平或者竖直)上相邻的1组成的区域。
    任何矩阵以外的区域都视为水域。如果 grid2的一个岛屿,被 grid1的一个岛屿完全 包含,也就是说 grid2中该岛屿的每一个格子都被 grid1中同一个岛屿完全包含,
    那么我们称 grid2中的这个岛屿为 子岛屿。请你返回 grid2中 子岛屿的 数目。
    示例 1:
        输入:grid1 = [[1,1,1,0,0],[0,1,1,1,1],[0,0,0,0,0],[1,0,0,0,0],[1,1,0,1,1]], grid2 = [[1,1,1,0,0],[0,0,1,1,1],[0,1,0,0,0],[1,0,1,1,0],[0,1,0,1,0]]
        输出:3
        解释:如上图所示,左边为 grid1 ,右边为 grid2 。
        grid2 中标红的 1 区域是子岛屿,总共有 3 个子岛屿。
    题眼:读懂题很重要:grid2的岛屿,对应到grid1位置都为1的话,说明就是个grid1的子岛屿;从这个角度看,有点类似于飞地(“1020. 飞地的数量”)的判断了
    思路:第一步、正常访问并跟踪grid2的岛屿
         第二步、用一个标记判断跟踪的岛屿是否是 子岛屿(grid1对应位置处都是陆地就是子岛屿:gird2岛屿的正常遍历确保grid1的对应位置是一个岛屿),
         但是一定要把整个岛屿遍历完,不能加break提前跳出(存在可能使得剩余没有遍历的位置构成满足条件的子岛屿了)
    '''
    
    
    class Solution:
        def countSubIslands(self, grid1: List[List[int]], grid2: List[List[int]]) -> int:
            # 读懂题很重要:grid2的岛屿,对应到grid1种位置都为1的话,说明就是个grid1的子岛屿
            # 从这个角度看,有点类似于飞地的判断了
            result = 0
            que = deque()
            m, n = len(grid2), len(grid2[0])
            visited = [[False] * n for _ in range(m)]
            for i in range(m):
                for j in range(n):
                    if grid2[i][j] == 1 and not visited[i][j]:
                        que.append((i, j))
                        visited[i][j] = True
                        isSub = True
                        while len(que) > 0:
                            x, y = que.popleft()
                            if isSub:
                                if grid1[x][y] == 0:
                                    isSub = False
                            for tx, ty in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
                                if 0 <= tx < m and 0 <= ty < n and not visited[tx][ty] and grid2[tx][ty] == 1:
                                    que.append((tx, ty))
                                    visited[tx][ty] = True
                        if isSub:
                            result += 1
            return result
    
    
    if __name__ == "__main__":
        obj = Solution()
        while True:
            try:
                in_line = input().strip().split('=')
                grid1, grid2 = [], []
                for row in in_line[1].strip().split('g')[0][1: -3].split(']')[: -1]:
                    grid1.append([int(n) for n in row.split('[')[1].split(',')])
                for row in in_line[2].strip()[1: -1].split(']')[: -1]:
                    grid2.append([int(n) for n in row.split('[')[1].split(',')])
                print(obj.countSubIslands(grid1, grid2))
            except EOFError:
                break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    695. 岛屿的最大面积

    “200. 岛屿数量”的扩展,求解“连通域”的最大面积,正常广度优先搜索方法基础上,额外添加 连通域内方块个数的累计即可——只需要一次遍历即可

    from typing import List, Optional, Union
    from collections import deque
    '''
    695. 岛屿的最大面积
    给你一个大小为 m x n 的二进制矩阵 grid 。
    岛屿是由一些相邻的1(代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设grid 的四个边缘都被 0(代表水)包围着。
    岛屿的面积是岛上值为 1 的单元格的数目。
    计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 
    示例 1:
        输入:grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]]
        输出:6
        解释:答案不应该是 11 ,因为岛屿只能包含水平或垂直这四个方向上的 1 。
    题眼:“200. 岛屿数量”的扩展,求解“连通域”的最大面积,正常BFS遍历,添加上 连通域内方块个数的累计即可——只需要一次遍历即可
    按照这个思路改成DFS时,觉得还是不那么好理解!
    '''
    
    
    class Solution:
        def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
            # 广度优先搜索,在有返回值的时候,可能更好理解
            # 情况1、矩阵为空
            if not grid:
                return 0
            # 情况2、矩阵不为空
            que = deque()
            m, n = len(grid), len(grid[0])
            visited = [[False] * n for _ in range(m)]
            result = 0
            for i in range(m):
                for j in range(n):
                    if grid[i][j] == 1 and not visited[i][j]:
                        que.append((i, j))
                        visited[i][j] = True
                        temp = 1
                        while len(que) > 0:
                            x, y = que.popleft()
                            for tx, ty in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
                                if 0 <= tx < m and 0 <= ty < n and not visited[tx][ty] and grid[tx][ty] == 1:
                                    que.append((tx, ty))
                                    visited[tx][ty] = True
                                    temp += 1
                        result = max(result, temp)
            return result
    
            # # 深度优先搜索,在有返回值的时候,感觉并不是特别理解
            # # 特殊情况:矩阵为空
            # if not grid:
            #     return 0
            # m, n = len(grid), len(grid[0])
            # visited = [[False] * n for _ in range(m)]
            # maxArea = 0
            # # 搜索函数
            # def dfs(x, y):
            #     if not 0 <= x < m or not 0 <= y < n or visited[x][y] or grid[x][y] != 1:  # 非法访问、重复访问、水体
            #         return 0
            #     visited[x][y] = True
            #     # ans = 1
            #     n1 = dfs(x + 1, y)
            #     n2 = dfs(x - 1, y)
            #     n3 = dfs(x, y - 1)
            #     n4 = dfs(x, y + 1)
            #     return 1 + n1 + n2 + n3 + n4
            # for i in range(m):
            #     for j in range(n):
            #         if grid[i][j] == 1 and visited[i][j] == False:  # 遇到岛屿,并且是第一次访问
            #             area = dfs(i, j)
            #             maxArea = max(maxArea, area)
            # return maxArea
    
    
    if __name__ == "__main__":
        obj = Solution()
        while True:
            try:
                in_line = input().strip().split('=')[1].strip()[1: -1]
                grid = []
                for row in in_line.split(']')[:-1]:
                    grid.append([int(i) for i in row.split('[')[1].split(',')])
                print(obj.maxAreaOfIsland(grid))
            except EOFError:
                break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    677 · 大岛的数量

    与“695. 岛屿的最大面积”差不多的一道题目,正常广度优先搜索方法基础上,额外添加 连通域内方块个数的累计即可——只需要一次遍历即可

    from typing import List, Optional, Union
    from collections import deque
    '''
    lintcode
    677 · 大岛的数量
    给一个布尔类型的二维数组, 0 表示海, 1 表示岛。如果两个1是相邻的,那么我们认为他们是同一个岛.我们只考虑 上下左右 相邻.
    找到大小在 k 及 k 以上的岛屿的数量
    示例 1:
        输入:
        [[1,1,0,0,0],[0,1,0,0,1],[0,0,0,1,1],[0,0,0,0,0],[0,0,0,0,1]]
        2
        输出: 2
        解释:
        2D网络为
        [[1, 1, 0, 0, 0],
         [0, 1, 0, 0, 1],
         [0, 0, 0, 1, 1],
         [0, 0, 0, 0, 0],
         [0, 0, 0, 0, 1]]
        一共有两个大小为3的岛。
    题眼:从这题 开始 统一用BFS做吧,因为DFS可能存在栈溢出,同时, 最短路径问题 也是用BFS做的,统一模板,方便记忆
    思路: 
    '''
    
    
    class Solution:
        def numsof_island(self, grid: List[List[int]], k: int) -> int:
            # 特殊情况
            if not grid:
                return 0
            m, n = len(grid), len(grid[0])
            dq = deque()  # 用BFS做
            result = 0
            visited = [[False]*n for _ in range(m)]
            for i in range(m):
                for j in range(n):
                    if grid[i][j] == 1 and not visited[i][j]:  # 第一次被访问的岛屿
                        dq.append((i, j))  # 加入队列,并立刻标记
                        visited[i][j] = True
                        curArea = 1
                        while dq:  # BFS
                            x, y = dq.popleft()
                            for tx, ty in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
                                if 0 <= tx < m and 0 <= ty < n and grid[tx][ty] == 1 and not visited[tx][ty]:  # 有效、是岛屿、第一次访问
                                    dq.append((tx, ty))  # 加入队列,并立刻标记
                                    visited[tx][ty] = True
                                    curArea += 1
                        if curArea >= k:
                            result += 1
            return result
    
    
    if __name__ == "__main__":
        obj = Solution()
        while True:
            try:
                in_line = input().strip()[1: -1].split(']')[: -1]
                grid = []
                for row in in_line:
                    grid.append([int(i) for i in row.split('[')[1].split(',')])
                k = int(input().strip())
                print(obj.numsof_island(grid, k))
            except EOFError:
                break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    827. 最大人工岛

    “695. 岛屿的最大面积”再加上 0变1 后的最大面积判断:需要额外添加 简单数字的岛屿标记矩阵及岛屿标记-面积哈希表

    from typing import List, Optional, Union
    from collections import deque
    '''
    827. 最大人工岛
    给你一个大小为 n x n 二进制矩阵 grid 。最多 只能将一格0 变成1 。
    返回执行此操作后,grid 中最大的岛屿面积是多少?
    岛屿 由一组上、下、左、右四个方向相连的1 形成。
    示例 1:
        输入: grid = [[1, 0], [0, 1]]
        输出: 3
        解释: 将一格0变成1,最终连通两个小岛得到面积为 3 的岛屿。
    题眼:“695. 岛屿的最大面积”再加上 0变1 后的最大面积判断:需要额外添加 简单数字的岛屿标记矩阵及岛屿标记-面积哈希表
    思路:第一步,求出 岛屿面积 并对岛屿进行简单数字的标记(与哈希表的key值相同,便于直接取出value值,而不是再for循环一次),建立 岛屿标记-面积 哈希字典存储
         第二步,遍历0,将其变为1后,求能连接的最大岛屿面积 
    '''
    
    
    class Solution:
        def largestIsland(self, grid: List[List[int]]) -> int:
            # 需要两次遍历,一次获取所有岛屿及其面积,二次对0变1看能连接几个岛屿——加一个岛屿标记矩阵能够在不改变原矩阵的情况下不超时
            # 当然,这个题直接在原矩阵中标记是最方便的,还可以省去visited矩阵
            # 特殊情况
            if not grid:
                return 0
            que = deque()
            m, n = len(grid), len(grid[0])
            visited = [[False] * n for _ in range(m)]
            marked = [[-1] * n for _ in range(m)]
            mark_num = 0
            zeros = []
            islands = {}
            result = 0  # 在一次遍历中成为单个最大的岛屿
            for i in range(m):
                for j in range(n):
                    if grid[i][j] == 0:
                        zeros.append((i, j))
                    elif grid[i][j] == 1 and not visited[i][j]:
                        que.append((i, j))
                        visited[i][j] = True
                        marked[i][j] = mark_num
                        temp = 1  # 临时变量,统计当前岛屿面积
                        while len(que) > 0:
                            x, y = que.popleft()
                            for tx, ty in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
                                if 0 <= tx < m and 0 <= ty < n and not visited[tx][ty] and grid[tx][ty] == 1:
                                    que.append((tx, ty))
                                    visited[tx][ty] = True
                                    marked[tx][ty] = mark_num
                                    temp += 1
                        islands[mark_num] = temp
                        mark_num += 1
                        result = max(result, temp)
            # 二次对0变1看能连接几个岛屿
            for x, y in zeros:
                match_keys = set()  # 避免加入重复的key
                for tx, ty in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
                    if 0 <= tx < m and 0 <= ty < n and marked[tx][ty] in islands:
                        match_keys.add(marked[tx][ty])
                temp = 1  # 细节:加上自己,初始值设置为1
                for k in match_keys:
                    temp += islands[k]
                result = max(result, temp)
            return result
    
            # 超时!
            # # 需要两次遍历,一次获取所有岛屿及其面积,二次对0变1看能连接几个岛屿
            # # 特殊情况
            # if not grid:
            #     return 0
            # que = deque()
            # m, n = len(grid), len(grid[0])
            # visited = [[False] * n for _ in range(m)]
            # zeros = []
            # islands = {}
            # result = 0  # 在一次遍历中成为单个最大的岛屿
            # for i in range(m):
            #     for j in range(n):
            #         if grid[i][j] == 0:
            #             zeros.append((i, j))
            #         elif grid[i][j] == 1 and not visited[i][j]:
            #             que.append((i, j))
            #             visited[i][j] = True
            #             key = [(i, j)]
            #             while len(que) > 0:
            #                 x, y = que.popleft()
            #                 for tx, ty in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
            #                     if 0 <= tx < m and 0 <= ty < n and not visited[tx][ty] and grid[tx][ty] == 1:
            #                         que.append((tx, ty))
            #                         visited[tx][ty] = True
            #                         key.append((tx, ty))
            #             islands[tuple(key)] = len(key)
            #             result = max(result, len(key))
            # # 二次对0变1看能连接几个岛屿
            # for x, y in zeros:
            #     match_keys = set()  # 避免加入重复的key
            #     for tx, ty in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
            #         if 0 <= tx < m and 0 <= ty < n:
            #             for key in islands:  # 这里再套for循环取对应的岛屿标记,使得最后答案超时了
            #                 if (tx, ty) in key:
            #                     match_keys.add(key)
            #                     break
            #     temp = 1
            #     for k in match_keys:
            #         temp += islands[k]
            #     result = max(result, temp)
            # return result
    
    
    if __name__ == "__main__":
        obj = Solution()
        while True:
            try:
                in_line = input().strip().split('=')[1].strip()[1: -1]
                grid = []
                for row in in_line.split(']')[: -1]:
                    grid.append([int(i) for i in row.split('[')[1].split(',')])
                print(obj.largestIsland(grid))
            except EOFError:
                break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119

    860 · 不同岛屿的个数

    需要在判断岛屿个数的基础上,把相同形状的岛屿去重,也就是需要在遍历岛屿的基础上,增加hashset标记路径相对岛屿起始点的路径:因为对矩阵的遍历总是从左上到右下的,因此相同形状的岛屿起点总是位于左上角,添加的相对路径也能对应相等,最终hashset就是岛屿个数

    from typing import List, Optional, Union
    from collections import deque
    '''
    lintcode
    860 · 不同岛屿的个数
    给定一个由0和1组成的非空的二维网格,一个岛屿是指四个方向(包括横向和纵向)都相连的一组1(1表示陆地)。你可以假设网格的四个边缘都被水包围。
    找出所有不同的岛屿的个数。如果一个岛屿与另一个岛屿形状相同(不考虑旋转和翻折),我们认为这两个岛屿是相同的。
    示例 1:
        输入:
        [[1,1,0,0,0],[1,1,0,0,0],[0,0,0,1,1],[0,0,0,1,1]]
        输出:1
        解释:一共有两个岛,但是两个岛的形状相同
        输入:[[1,1,0,0,1],[1,0,0,0,0],[1,1,0,0,1],[0,1,0,1,1]]
        输出:3
    题眼:
    思路:需要在判断岛屿个数的基础上,把相同形状的岛屿去重,也就是需要在遍历岛屿的基础上,增加hashset标记路径(相对岛屿起始点的路径:因为对矩阵的遍历总是
    从左上到右下的,因此相同形状的岛屿起点总是位于左上角,添加的相对路径也能对应相等),最终hashset就是岛屿个数
    '''
    
    
    class Solution:
        def numberof_distinct_islands(self, grid: List[List[int]]) -> int:
            # 特殊情况
            if not grid:
                return 0
            m, n = len(grid), len(grid[0])
            dq = deque()  # 用BFS做
            island = set()  # set自动去重
            visited = [[False]*n for _ in range(m)]
            for i in range(m):
                for j in range(n):
                    path = []
                    if grid[i][j] == 1 and not visited[i][j]:  # 第一次被访问的岛屿
                        dq.append((i, j))  # 加入队列,并立刻标记
                        visited[i][j] = True
                        path.append(str((i-i, j-j)))  # 路径添加相对岛屿起点的坐标位置:用元组作为集合内元素也可以
                        while dq:  # BFS
                            x, y = dq.popleft()
                            for tx, ty in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
                                if 0 <= tx < m and 0 <= ty < n and grid[tx][ty] == 1 and not visited[tx][ty]:  # 有效、是岛屿、第一次访问
                                    dq.append((tx, ty))  # 加入队列,并立刻标记
                                    visited[tx][ty] = True
                                    path.append(str((tx-i, ty-j)))  # 路径添加相对岛屿起点的坐标位置
                        island.add(''.join(path))
            return len(island)
    
    
    if __name__ == "__main__":
        obj = Solution()
        while True:
            try:
                in_line = input().strip()[1: -1].split(']')[: -1]
                grid = []
                for row in in_line:
                    grid.append([int(i) for i in row.split('[')[1].split(',')])
                print(obj.numberof_distinct_islands(grid))
            except EOFError:
                break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    804 · 不同岛屿的个数II

    需要在判断岛屿个数的基础上,把相同形状(加上旋转和翻转,一共8种)的岛屿去重;对于每个形状的岛屿,都把原始位置添加上,经过正则函数返回该岛屿形状对应的唯一标识,正则函数逻辑:添加该形状的8种旋转、翻转变化,然后对每种形状各自排序后取最小值(排序这个逻辑不是很理解)

    from typing import List, Optional, Union
    from collections import deque
    '''
    lintcode
    804 · 不同岛屿的个数II
    给定一个由0和1组成的非空的二维网格,一个岛屿是指四个方向(包括横向和纵向)都相连的一组1(1表示陆地)。你可以假设网格的四个边缘都被水包围。
    计算不同岛屿的数量。当一个岛被认为与另一个岛相同时,它们有相同的形状,或在旋转后的形状相同(90,180,或270度)或翻转(左/右方向或向上/向下方向)。
    示例 1:
        输入:
        [[1,1,0,0,0],[1,0,0,0,0],[0,0,0,0,1],[0,0,0,1,1]]
        输出:1
        解释:一共有两个岛,但是两个岛旋转后的形状相同
    题眼:
    思路:需要在判断岛屿个数的基础上,把相同形状(加上旋转和翻转,一共8种)的岛屿去重;对于每个形状的岛屿,都把原始位置添加上,经过正则函数返回该岛屿形状
    对应的唯一标识,正则函数逻辑:添加该形状的8种旋转、翻转变化,然后对每种形状各自排序后取最小值(排序这个逻辑不是很理解)
    '''
    
    
    class Solution:
        def num_distinct_islands2(self, grid: List[List[int]]) -> int:
            # 特殊情况
            if not grid:
                return 0
            m, n = len(grid), len(grid[0])
            dq = deque()  # 用BFS做
            island = set()  # set自动去重
            visited = [[False]*n for _ in range(m)]
            for i in range(m):
                for j in range(n):
                    shape = []
                    if grid[i][j] == 1 and not visited[i][j]:  # 第一次被访问的岛屿
                        dq.append((i, j))  # 加入队列,并立刻标记
                        visited[i][j] = True
                        shape.append((i, j))  # 添加岛屿的第一个位置
                        while dq:  # BFS
                            x, y = dq.popleft()
                            for tx, ty in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
                                if 0 <= tx < m and 0 <= ty < n and grid[tx][ty] == 1 and not visited[tx][ty]:  # 有效、是岛屿、第一次访问
                                    dq.append((tx, ty))  # 加入队列,并立刻标记
                                    visited[tx][ty] = True
                                    shape.append((tx, ty))  # 添加岛屿的其他位置
                        island.add(self.canonical(shape))  # 添加正则化后的岛屿
            return len(island)
    
        def canonical(self, shape):  # 这一部分老是写不对
    
            def encode(shape):
                x, y = shape[0]
                return "".join(str(i - x) + ':' + str(j - y) for i, j in shape)  # 减去起始点取相对位置是对排序后的形状进行的
    
            shapes = [[(a * i, b * j) for i, j in shape] for a, b in ((1, 1), (1, -1), (-1, 1), (-1, -1))]  # 添加 翻转
            shapes += [[(j, i) for i, j in shape] for shape in shapes]  # 添加 旋转
    
            return min(encode(sorted(shape)) for shape in shapes)  # 返回排序后的结果
    
    
    if __name__ == "__main__":
        obj = Solution()
        while True:
            try:
                in_line = input().strip()[1: -1].split(']')[: -1]
                grid = []
                for row in in_line:
                    grid.append([int(i) for i in row.split('[')[1].split(',')])
                print(obj.num_distinct_islands2(grid))
            except EOFError:
                break
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
  • 相关阅读:
    Excel里实现 sum(case when xxx then xxx else xxx end)的用法
    ScanContext 论文详解 - 用途:Lidar SLAM 回环检测、空间描述符
    JVM 内存布局详解(荣耀典藏版)
    深度学习——目标检测基础知识
    [M数学] lc2834. 找出美丽数组的最小和(数学+找规律+思维)
    初识图学习
    showdoc本地部署后如何关闭注册功能
    基于TCP/UDP协议的Socket编程方法
    前端项目如何找到所有未被引用的文件
    Learn Prompt-ChatGPT 精选案例:学习各国语言
  • 原文地址:https://blog.csdn.net/qq_39975984/article/details/132911879