【并查集】B001_岛屿数量(并查集 | DFS | BFS)

一、题目描述

Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. 
An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. 
You may assume all four edges of the grid are all surrounded by water.

Example 1:
Input:
11110
11010
11000
00000

Output: 1

Example 2:
Input:
11000
11000
00100
00011

Output: 3

二、题解

核心思想:遍历二维网格 g r i d grid ,将竖直或水平相邻的陆地 u n i o n union 。最后,返回并查集 U F UF 中的连通分量 c o u n t count

难点列举如下:

  • 朋友圈 不一样的是,这题的连通分量 c o u n t count 初始值为 0,因为一开始并没有找到陆地。
  • 因为网格 g r i d grid 是二维数组,数组 p a r e n t r a n k parent、rank 的也大小需要变为网格的大小 M N M * N
  • 在初始化并查集 U F UF 时,由于网格是二维的, p a r e n t r a n k parent、rank 数组的的索引要唯一,这个小算法为 i n d e x = i c o l s + j index = i*cols + j
    • 其中,排名数组 r a n k rank 的初始值为 0;
    • 当当前遍历结点 ( i , j ) (i, j) 为陆地时,根节点数组 p a r e n t parent 初始值为 i c o l s + j i*cols + j ,代表以每一个陆地 ( i , j ) (i,j) 初始化一棵树。
  • n u m I s l a n d s numIslands 方法中,只要出现陆地,就将该陆地周围的陆地 u n i o n union 起来。如下所示 —>
  • 最后返回的是 U F UF 连通分量的个数 c o u n t count

(1) 并查集

class Solution {
  /**
   * @thought:并查集
   * @date: 1/20/2020 9:59 PM
   * @Execution info:6ms 击败 27% 的j,MB 击败 7.28% 的j
   * @Asymptotic Time Complexity:O()
   */
  public int numIslands(char[][] grid) {
    if(grid.length == 0 || grid == null)  return 0;
    int ans=0;
    UF uf = new UF(grid);
    // 目标是将所有相邻的岛屿连通起来,最后求uf的连通分量个数
    int rows = grid.length;
    int cols = grid[0].length;

    for (int i = 0; i < rows; i++)
    for (int j = 0; j < cols; j++)
    if(grid[i][j] == '1') {

      grid[i][j] = '0'; // 标记为访问过
      int u = i-1, d = i+1, l = j-1, r = j+1;
      int t = i*cols + j;

      if(u >= 0 && grid[u][j] == '1')
        uf.union(t, u*cols + j);

      if(d < rows && grid[d][j] == '1')
        uf.union(t, d*cols + j);

      if(l >= 0 && grid[i][l] == '1')
        uf.union(t, i*cols + l);

      if(r < cols && grid[i][r] == '1')
        uf.union(t, i*cols + r);
    }

    return uf.count;
  }

  class UF {
    private int[] parent;
    private int[] rank;
    private int count;  // 连通分量
    public UF(char[][] grid) {
      count = 0;
      int rows = grid.length;
      int cols = grid[0].length;
      parent = new int[rows * cols];
      rank = new int[rows * cols];

      for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
          int t = i*cols + j;
          rank[t] = 0; // 以(i,j)为根的树的深度暂时为0;
          // 如果该点时岛屿,则初始化根
          if(grid[i][j] == '1') {
            parent[t] = t;  // 为了让下标不重复
            ++count;        // 连通分量增加1
          }
        }
      }
    }

    public int getCount() {return count;}

    // 找到结点p对应的组
    public int find(int p) {
      if(p < 0 || p > parent.length)
        throw new IllegalArgumentException("p is out of bound");
      // 路径压缩
      while(p != parent[p]) {
        parent[p] = parent[parent[p]];
        p = parent[p];
      }
      return p;
    }

    public boolean isConnected(int p, int q) {
      return find(p) == find(q);
    }

    public void union(int p, int q) {
      int pRootID = find(p);
      int qRootID = find(q);

      if(pRootID == qRootID)  return;

      if(rank[pRootID] > rank[qRootID])
        parent[qRootID] = pRootID;
      else if(rank[pRootID] < rank[qRootID])
        parent[pRootID] = qRootID;
      else {
        parent[pRootID] = qRootID;
        rank[qRootID]++;  // 深度加一
      }
      count--;
    }
  }
}

复杂度分析

  • 时间复杂度: O ( M N ) O(M*N) ,其中 M M N N 分别为网格 g r i d grid 行数和列数。
  • 空间复杂度: O ( 2 M N ) O(2*M*N) p a r e n t r a n k parent、rank 数组花费主要的空间。
发布了300 篇原创文章 · 获赞 48 · 访问量 8043

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/104057158