zoukankan      html  css  js  c++  java
  • 力扣200. 岛屿数量

     下面这题我刚开始一直以为是求图的连通分量的个数,弄了好久发现总是有问题,后来才发现不是连通分量的题型,连通分量求的是顶点的被分成多少块,下面这种题目是一个矩阵被分成多少块,两者不一样

    200. 岛屿数量

    给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
    岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。
    此外,你可以假设该网格的四条边均被水包围。
     示例 1:

    输入:
    11110
    11010
    11000
    00000
    输出: 1

    示例 2:

    输入:
    11000
    11000
    00100
    00011
    输出: 3
    解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。

    思路一:dfs

    我们可以将二维网格看成一个无向图,竖直或水平相邻的 1 之间有边相连。
    为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 1,则以其为起始节点开始进行深度优先搜索。在深度优先搜索的过程中,每个搜索到的 11 都会被重新标记为 00。
    最终岛屿的数量就是我们进行深度优先搜索的次数。

     1 class Solution {
     2    
     3     int rows, cols;
     4     // dfs遍历一个与(i,j)都相连的4个单元
     5     void dfs(char[][]grid, int x, int y){
     6         // 把grid[x][y]标记为0,即使设置为已访问
     7         grid[x][y] = '0';
     8 
     9         // 遍历四个方向的单元
    10         if(x - 1 >= 0 && grid[x-1][y] == '1'){
    11             dfs(grid, x-1, y);
    12         }
    13         if(x + 1 < rows && grid[x+1][y] == '1'){
    14             dfs(grid, x+1, y);
    15         }
    16         
    17         if(y - 1 >= 0 && grid[x][y-1] == '1'){
    18             dfs(grid, x, y-1);
    19         }
    20         if(y + 1 < cols && grid[x][y+1] == '1'){
    21             dfs(grid, x, y+1);
    22         }
    23     }
    24 
    25 
    26     public int numIslands(char[][] grid) {
    27         if(grid == null || (grid != null && grid.length == 0))
    28             return 0;
    29 
    30         rows = grid.length;
    31         cols = grid[0].length;
    32         
    33         int cnt = 0;
    34         // dfs遍历一个连通分量
    35         for(int i = 0; i < rows; i++){
    36             for(int j = 0; j < cols; j++){
    37                 if(grid[i][j] == '1'){
    38                     cnt++;
    39                     dfs(grid, i, j);
    40                 }
    41             }
    42         }
    43         return cnt;
    44     }
    45 }

    力扣测试时间为1ms, 空间为42.4MB

    复杂度分析:

    时间复杂度:numIslands()函数中的双重循环其实只有少部分会进入dfs进行递归,不进入递归的迭代相比于进入迭代的情况花费的时间是非常短的,所以主要看进入递归的那些位置,而对每个位置为‘1‘的单元都进行了一次dfs()递归遍历,无论是在双重循环还是在dfs()内部,所以dfs()的调用次数可以作为时间复杂度的一个评定指标,dfs()的调用次数等于‘1‘的个数,'1'的个数最多为N*M, 所以时间复杂度为O(M*N)

    空间复杂度:当所有位置都是1的时候,递归的深度最大为M*N, 所以空间复杂度为O(M*N)

    思路二:BFS

    我们可以扫描整个二维网格。如果一个位置为 1,则以其为起始节点开始进行广度优先搜索。在广度优先搜索的过程中,每次把位置加入队列后就把这个位置置为'0'

     1 class Solution {
     2    
     3     public int numIslands(char[][] grid) {
     4         if(grid == null || (grid != null && grid.length == 0))
     5             return 0;
     6 
     7         int rows = grid.length;
     8         int cols = grid[0].length;
     9         
    10         int cnt = 0;
    11         
    12         for(int i = 0; i < rows; i++){
    13             for(int j = 0; j < cols; j++){
    14                 if(grid[i][j] == '1'){      // 如果位置为1,利用BFS,搜索整个相连的所有单元
    15                     cnt++;
    16                     System.out.println(i + "	" + j);
    17                     Queue<Pair<Integer, Integer>> queue = new LinkedList<>();
    18                     Pair<Integer, Integer> pair = new Pair<>(i, j);
    19                     queue.offer(pair);
    20                     grid[i][j] = '0';
    21                     while(!queue.isEmpty()){
    22                         pair = queue.poll();
    23                         int row = pair.getKey();
    24                         int col = pair.getValue();
    25                         // 把四个方向相连的单元都添加到队列中
    26                         if(row - 1 >= 0 && grid[row-1][col] == '1'){
    27                             queue.offer(new Pair<>(row-1, col));
    28                             grid[row-1][col] = '0';
    29                         }
    30                         if(row + 1 < rows && grid[row+1][col] == '1'){
    31                             queue.offer(new Pair<>(row+1, col));
    32                             grid[row+1][col] = '0';
    33                         }
    34                         if(col - 1 >= 0 && grid[row][col-1] == '1'){
    35                             queue.offer(new Pair<>(row, col-1));
    36                             grid[row][col-1] = '0';
    37                         }
    38                         if(col + 1 < cols && grid[row][col+1] == '1'){
    39                             queue.offer(new Pair<>(row, col+1));
    40                             grid[row][col+1] = '0';
    41                         }
    42                         System.out.println(queue.size());
    43 
    44                     }
    45                 }
    46             }
    47         }
    48         return cnt;
    49     }
    50 }

    力扣测试时间为97ms, 空间为42.9MB, 太慢了

    复杂度分析:

    时间复杂度和思路一一样

    空间复杂度:O(min(M,N)),在最坏情况下,整个网格均为陆地,队列的大小可以达到min(M, N)

    思路三:并查集

    将位置为(i,j)的坐标映射为parent数组的i * cols + j下标的元素,这样每个位置都能一一映射一个parent[]元素,从而可以改变其父节点

     1 // 并查集实现
     2 class Solution {
     3     class UnionFind{
     4         int rows;       // 矩阵的行数
     5         int cols;       // 矩阵的列数
     6         int[] parent;   // 并查集数组
     7         int[] rank;     // 存储每个数字所在树的高度,有助于降低树的高度
     8         int count = 0;  // 记录岛屿数量
     9 
    10         // 初始化parent[][]数组为下标本身,count值为1的个数
    11         public UnionFind(char[][] grid){
    12             rows = grid.length;
    13             cols = grid[0].length;
    14             parent = new int[rows * cols];
    15             rank = new int[rows * cols];
    16             for(int i = 0; i < rows; i++){
    17                 for(int j = 0; j < cols; j++){
    18                     if(grid[i][j] == '1'){
    19                         count++;
    20                     }
    21                     parent[cols * i + j] = cols * i + j;
    22                     rank[cols * i + j] = 0;
    23                 }
    24             }
    25         }
    26 
    27         // 查找父节点
    28         int findFather(int i){
    29             if(parent[i] != i){
    30                 parent[i] = parent[parent[i]];  // 路径压缩
    31             }
    32             return parent[i];
    33         }
    34 
    35         // 合并两个点
    36         public void union(int x, int y){
    37             // 查找两个数字的父节点
    38             int xfather = findFather(x);
    39             int yfather = findFather(y);
    40 
    41             // 如果不是同一个则合并,合并的时候矮的树挂到高的树上
    42             if(xfather != yfather){
    43                 if(rank[xfather] > rank[yfather]){
    44                     parent[yfather] = xfather;
    45                 }else if(rank[xfather] < rank[yfather]){
    46                     parent[xfather] = yfather;
    47                 }else{
    48                     parent[xfather] = yfather;
    49                     rank[yfather]++;        // 树的高度加一
    50                 }
    51                 count--;        // 每合并两个结点,count就减一
    52             }
    53 
    54         }
    55 
    56         int getCount(){
    57             return count;
    58         }
    59     }
    60 
    61    
    62     public int numIslands(char[][] grid) {
    63         if(grid == null || (grid != null && grid.length == 0))
    64             return 0;
    65 
    66         int rows = grid.length;
    67         int cols = grid[0].length;
    68         
    69         UnionFind uf = new UnionFind(grid);
    70         for(int i = 0; i < rows; i++){
    71             for(int j = 0; j < cols; j++){  // 如果为1,则连接所有为1的四周结点
    72                 if(grid[i][j] == '1'){
    73                     grid[i][j] = '0';
    74                     if(i - 1 >= 0 && grid[i - 1][j] == '1')
    75                         uf.union(i * cols + j, (i - 1) * cols + j);
    76                     if(i + 1 < rows && grid[i + 1][j] == '1')
    77                         uf.union(i * cols + j, (i + 1) * cols + j);
    78                     if(j - 1 >= 0 && grid[i][j - 1] == '1')
    79                         uf.union(i * cols + j, i * cols + j - 1);
    80                     if(j + 1 < cols && grid[i][j + 1] == '1')
    81                         uf.union(i * cols + j, i * cols + j + 1);
    82                 }
    83             }
    84         }
    85         
    86         return uf.getCount();     
    87     }
    88 }

    力扣测试时间为7ms, 空间为42.5MB

    复杂度分析:

    时间复杂度:取决于矩阵式中'1'的个数,所以时间复杂度为O(N*M)

    空间复杂度:两个矩阵rank[]和parent[],数组长度为N*M, 所以空间复杂度为O(N*M);

    思路参考:

    https://leetcode-cn.com/problems/number-of-islands/solution/dao-yu-shu-liang-by-leetcode/

  • 相关阅读:
    倚天
    第5課 森さんは7時に起きます。
    第1課 李さんは中国人です
    一个整体的规划
    こんにちは
    原始凭证
    第3課 ここはデパートです
    ERP术语
    第2課 これはほんです
    vc 问题总结
  • 原文地址:https://www.cnblogs.com/hi3254014978/p/13081049.html
Copyright © 2011-2022 走看看