[路飞]_leetcode刷题_200. 岛屿数量

1,294 阅读4分钟

题目

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

解法一

思路:

图表深度优先搜索。

按照套路我们有以下几步,

  1. 申明一个Set对象visited,用于存储节点是否有被遍历过
  2. 申明一个count用来记录岛屿的数量。
  3. 遍历这个图表,如果某个节点值为1,则岛屿的数量+1,并且去递归的遍历它的上下左右,如果周围相邻节点未被遍历过,且值为1,则继续递归的遍历这个相邻节点的上下左右。
  4. 最终返回岛屿数量count。

代码如下:

/**
 * @param {character[][]} grid
 * @return {number}
 */
var numIslands = function(grid) {
    if(!grid || grid.length ==0){
        return 0;
    }
    let height = grid.length;
    let width = grid[0].length;
    let visited = [];
    for(let i=0;i<height;i++){
        visited[i] = []
    }
    let count = 0;
    for(let i=0;i<height;i++){
        for(let j=0;j<width;j++){
            if(grid[i][j] == 1 && !visited[i][j]){
                count++;
                dfs(grid,visited,i,j);
            }
        }
    }
    return count;
};

function dfs(grid,visited,i,j){
    let height = grid.length;
    let width = grid[0].length;
    if(i<0 || i>height-1||j<0||j>width-1||visited[i][j] || grid[i][j] == 0){
        return ;
    }
    visited[i][j] = true;
    dfs(grid,visited,i,j+1);
    dfs(grid,visited,i,j-1);
    dfs(grid,visited,i+1,j);
    dfs(grid,visited,i-1,j);
}

复杂度分析

时间复杂度:O(M*N),M和N分别为矩阵的高和宽

空间复杂度:O(M*N),存放visted的数组为M*N,另外,当全部为陆地的时候,递归栈的最大高度为M*N。

ps:这里其实是可以优化的,不用visited,直接判断当前节点为0则不遍历,然后每遍历一个陆地之后,将它的值改为0即可。我是为了套一下图表深度优先遍历的模板,用一个visited,大家好理解。

解法二

思路:

广度优先遍历。

  1. 申明一个队列,用于存放节点值为1的相邻节点。
  2. 申明一个变量count,用于存放岛屿数量。
  3. 遍历grid每一个节点,如果节点值为1,将该节点推入队列,并且count+1。
  4. 将队列头节点弹出,如果该节点的上下左右有节点为1,则将该相邻节点推入队列。
  5. 只要队列不为空,就一直操作,并且没处理一个节点,都要记得把值改为0
  6. 最终count即位岛屿数量。

代码如下:

/**
 * @param {character[][]} grid
 * @return {number}
 */
var numIslands = function(grid) {
    if(!grid || grid.length ==0){
        return 0;
    }
    let height = grid.length;
    let width = grid[0].length;
    let count = 0;
    let queue = [];
    for(let i=0;i<height;i++){
        for(let j=0;j<width;j++){
            if(grid[i][j] == 1){
                count++;
                queue.push([i,j])
                while(queue.length>0){
                    let node = queue.shift();
                    let p = node[0],q = node[1];
                    grid[p][q]=0;
                    offerToQueue(grid,queue,p-1,q);
                    offerToQueue(grid,queue,p+1,q);
                    offerToQueue(grid,queue,p,q-1);
                    offerToQueue(grid,queue,p,q+1);
                }
            }
        }
    }

    return count;
};

function offerToQueue(grid,queue,i,j){
    let height = grid.length;
    let width = grid[0].length;
    if(i<0 || j<0 || i>height-1||j>width-1 || grid[i][j] == 0){
        return ;
    }
    queue.push([i,j]);
    grid[i][j]=0;
}

复杂度分析:

时间复杂度:O(M*N),M和N分别为矩阵的高和宽。

空间复杂度:O(M*N),M和N分别为矩阵的高和宽。

解法三

思路:

并查集。

我们可以利用并查集,先将节点都处理一遍,构建初始化父子关系,并初始化连通量为陆地数量。然后遍历所有节点,如果当前节点为1,则去看它的上下左右节点,如果有1,就合并两个节点,并且连通量-1,最终返回联通量即可。

代码如下:

/**
 * @param {character[][]} grid
 * @return {number}
 */
var numIslands = function(grid) {
    if(!grid || grid.length ==0){
        return 0;
    }
    let height = grid.length;
    let width = grid[0].length;
    let count = 0;
    let parent = new Map();
    // 先处理parent,值为1的放入parent
    for(let i=0;i<height;i++){
        for(let j=0;j<width;j++){
            if(grid[i][j] == 1){
                let node = [i,j];
                parent.set(`${i}_${j}`,node);
                count++;
            }
        }
    }
    for(let i=0;i<height;i++){
        for(let j=0;j<width;j++){
            // 如果值为1,先将它自己的值设为0,再去找它的上下左右,如果找到了,就合并,并且count--
             if(grid[i][j] == 1){
                grid[i][j] = 0;
                union(grid,parent,[i,j],[i,j+1])==1 && count--
                union(grid,parent,[i,j],[i,j-1])==1 && count--
                union(grid,parent,[i,j],[i-1,j])==1 && count--
                union(grid,parent,[i,j],[i+1,j])==1 && count--
             }
        }
    }
    return count;
};
function union(grid,parent,p,q){
    let height = grid.length;
    let width = grid[0].length;
    if(q[0]<0 || q[1]<0 || q[0]>height-1||q[1]>width-1 || grid[q[0]][q[1]] == 0){
        return ;
    }
    let rootP = find(parent,p)
    let rootQ = find(parent,q)
    if(rootP != rootQ){
        parent.set(`${rootP[0]}_${rootP[1]}`,rootQ)
        return 1;
    }
}

function find(parent,node){
    if(parent.get(`${node[0]}_${node[1]}`) != node){
        parent.set(`${node[0]}_${node[1]}`,find(parent,parent.get(`${node[0]}_${node[1]}`)))
    }
    return parent.get(`${node[0]}_${node[1]}`);
}

复杂度分析:

时间复杂度:O(MNlog(MN)),先要处理parent数组为MN,后续遍历grid,最坏情况全是岛屿,每个节点都需要遍历到为MN,此时find查找复杂度为logMN,故为O(MNlog(MN))

空间复杂度:O(M*N),最坏情况全是陆地,则需要parent需要存放所有的节点