LeetCode 岛屿数量(200题)DFS、BFS、并查集

LeetCode 岛屿数量

@author:Jingdai
@date:2020.12.29

题目描述(200题)

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例 :

输入:grid = [
 ["1","1","0","0","0"],
 ["1","1","0","0","0"],
 ["0","0","1","0","0"],
 ["0","0","0","1","1"]
]
输出:3

思路及代码

方法1 DFS和BFS

仔细阅读题目,可以发现其实题目就是求网格被 '0' 分成了几块,有几块就是有几个岛屿。如图,示例中的 '0' 将网格中的 '1' 分成了3块,结果就是3个岛屿。

在这里插入图片描述

根据此,我们可以对整个网格进行遍历,如果遇到 '1' ,就对它进行DFS或者BFS,将从它开始连通的 '1' 全部变成 '2' ,用 '2' 表示访问过的陆地。对网格遍历过程中,每新遇到一个 '1' ,表示一个新的岛屿,使岛屿数量加1。下面是具体代码。

DFS代码如下。

public int numIslands(char[][] grid) {
    
    
    int row = grid.length;
    int column = grid[0].length;
    int island = 0;
    for (int i = 0; i < row; i++) {
    
    
        for (int j = 0; j < column; j++) {
    
    
            if (grid[i][j] == '1') {
    
    
                island++;
                dfs(grid, i, j);
            }
        }
    }
    return island;
}

public void dfs(char[][] grid, int i, int j) {
    
    
    grid[i][j] = '2';
    int row = grid.length;
    int column = grid[0].length;
    // right  i    j+1
    // down   i+1  j
    // left   i    j-1
    // up     i-1  j

    if (j+1 < column && grid[i][j+1] == '1') {
    
    
        dfs(grid, i, j+1);
    } 
    if (i+1 < row && grid[i+1][j] == '1') {
    
    
        dfs(grid, i+1, j);
    } 
    if (j-1 >= 0 && grid[i][j-1] == '1') {
    
    
        dfs(grid, i, j-1);
    }
    if (i-1 >= 0 && grid[i-1][j] == '1') {
    
    
        dfs(grid, i-1, j);
    }
}

BFS代码如下。

public int numIslands(char[][] grid) {
    
    
    int row = grid.length;
    int column = grid[0].length;
    int island = 0;
    for (int i = 0; i < row; i++) {
    
    
        for (int j = 0; j < column; j++) {
    
    
            if (grid[i][j] == '1') {
    
    
                island++;
                bfs(grid, i, j);
            }
        }
    }
    return island;
}

public void bfs(char[][] grid, int i, int j) {
    
    
    // ** visit when enqueue
    int row = grid.length;
    int column = grid[0].length;
    // right  i    j+1
    // down   i+1  j
    // left   i    j-1
    // up     i-1  j
    LinkedList<int[]> queue = new LinkedList<>();
    queue.offer(new int[]{
    
    i, j});
    grid[i][j] = '2';
    while (queue.size() != 0) {
    
    
        int[] cur = queue.poll();
        if (cur[1]+1 < column && grid[cur[0]][cur[1]+1] == '1') {
    
    
            queue.offer(new int[]{
    
    cur[0], cur[1]+1});
            grid[cur[0]][cur[1]+1] = '2';
        }
        if (cur[0]+1 < row && grid[cur[0]+1][cur[1]] == '1') {
    
    
            queue.offer(new int[]{
    
    cur[0]+1, cur[1]});
            grid[cur[0]+1][cur[1]] = '2';
        }
        if (cur[1]-1 >= 0 && grid[cur[0]][cur[1]-1] == '1') {
    
    
            queue.offer(new int[]{
    
    cur[0], cur[1]-1});
            grid[cur[0]][cur[1]-1] = '2';
        }
        if (cur[0]-1 >= 0 && grid[cur[0]-1][cur[1]] == '1') {
    
    
            queue.offer(new int[]{
    
    cur[0]-1, cur[1]});
            grid[cur[0]-1][cur[1]] = '2';
        }
    }
}

方法2 并查集

这个题目也可以利用并查集数据结构来解决。还是对整个网格进行遍历,当遇到一个 '1' 时,我们把它旁边的4个方向的点都查看一下,看是否是 '1' ,如果是 '1',我们就将它们 union 起来。题目中要求岛屿的数目,就是求不连通的集合有几个,我们可以记录网格总共 '1' 的个数,用 '1' 的总数减去并查集中成功 union 的次数就是最后不连通的集合的个数。

扫描二维码关注公众号,回复: 12814153 查看本文章

其实还可以优化一下, 上面我们遇到一个 '1' 时,我们会把它旁边四个方向的点都查看一下,其实不用这么多。我们其实可以仅仅查看右边和下面,因为如果上面有的话那么它一定已经在访问上面那个点时连通过了,左边同理。最后的代码如下。

// Disjoint Set
private class DisjointSet {
    
    

    int[] parent;
    int jointNumber = 0;

    DisjointSet(int number) {
    
    
        parent = new int[number];
        Arrays.fill(parent, -1);
    }

    int find(int n) {
    
    
        int p = n;
        while (parent[p] >= 0) {
    
    
            p = parent[p];
        }
        int cur = n;
        int temp;
        // parent[cur] > 0 for initial is -1
        while (parent[cur] >= 0 && parent[cur] != p) {
    
    
            temp = parent[cur];
            parent[cur] = p;
            cur = temp;
        }
        return p;
    }

    void union(int x, int y) {
    
    
        int xParent = find(x);
        int yParent = find(y);
        if (xParent == yParent)
            return;
        parent[yParent] += parent[xParent]; 
        parent[xParent] = yParent;
        jointNumber++;
    }
}

public int numIslands(char[][] grid) {
    
    

    int row = grid.length;
    int column = grid[0].length;
    int lands = 0;

    DisjointSet disjointSet = new DisjointSet(row * column);

    for (int i = 0; i < row; i++) {
    
    
        for (int j = 0; j < column; j++) {
    
    
            if (grid[i][j] == '1') {
    
    
                lands++;
                // right
                if (j+1 < column && grid[i][j+1] == '1') {
    
    
                    disjointSet.union(i*column+j, i*column+j+1);
                }
                // under
                if (i+1 < row && grid[i+1][j] == '1') {
    
    
                    disjointSet.union(i*column+j, (i+1)*column+j);
                }
            }
        }
    }
    return lands - disjointSet.jointNumber;
}

猜你喜欢

转载自blog.csdn.net/qq_41512783/article/details/111936771