200岛屿数量

题目:给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。

来源:https://leetcode-cn.com/problems/number-of-islands/

法一:bfs 别人的代码

思路:先用一个bool数组用于记录标记过的每个位置,再依次遍历每个位置,一旦遇到一个1的区域,就把它全部遍历完,并把遍历过的记入bool数组中,本题的关键是一旦遇到一个区域就全部遍历完

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])
        # 制作标记矩阵,如果遍历过该点了,标记为True,本来为False
        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):
                # 只要是陆地,且没有被访问过的,就可以使用 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
View Code

法二:并查集

思路:遍历每个位置,如果当前位置为1,则向上和向左遍历两个位置,如果上面的位置为1,则以其为key,以当前位置为values进行记录,这样每个区域都有一个最高级别的values,最后把这个values提取出来,values的不同个数即为区域的个数。

from typing import List
class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        f = {}
        def find(x):
            f.setdefault(x, x)
            # 如果不相等,则说明之前已经将该位置进行标记为某一类了,
            if f[x] != x:
                f[x] = find(f[x])
            return f[x]
        # y是当前为1的位置,x是向上或向左为1的位置
        # 注意这个字典中,前面的key包含于后面的values,也就是说values是上级
        def union(x, y):
            f[find(x)] = find(y)
        if not grid: return 0
        row = len(grid)
        col = len(grid[0])
        for i in range(row):
            for j in range(col):
                if grid[i][j] == "1":
                    for x, y in [[-1, 0], [0, -1]]:
                        tmp_i = i + x
                        tmp_j = j + y
                        if 0 <= tmp_i < row and 0 <= tmp_j < col and grid[tmp_i][tmp_j] == "1":
                            union(tmp_i * col + tmp_j, i * col + j)
        print(f)
        res = set()
        # 这里是为了找到最上面的上级
        for i in range(row):
            for j in range(col):
                if grid[i][j] == "1":
                    res.add(find((i * col + j)))
        return len(res)
if __name__ == '__main__':
    grid = [['1', '0', '1'],
            ['1', '0', '1'],
            ['0', '1', '1'],]
    solution = Solution()
    result = solution.numIslands(grid)
    print(result)
View Code

ttt

 

posted on 2020-02-10 15:27  吃我一枪  阅读(163)  评论(0编辑  收藏  举报

导航