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

一、题目描述

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 grid,将竖直或水平相邻的陆地 u n i o n union union。最后,返回并查集 U F UF UF 中的连通分量 c o u n t count count

难点列举如下:

  • 与 朋友圈 不一样的是,这题的连通分量 c o u n t count count 初始值为 0,因为一开始并没有找到陆地。
  • 因为网格 g r i d grid grid 是二维数组,数组 p a r e n t 、 r a n k parent、rank parentrank 的也大小需要变为网格的大小 M ∗ N M * N MN
  • 在初始化并查集 U F UF UF 时,由于网格是二维的, p a r e n t 、 r a n k parent、rank parentrank 数组的的索引要唯一,这个小算法为 i n d e x = i ∗ c o l s + j index = i*cols + j index=icols+j
    • 其中,排名数组 r a n k rank rank 的初始值为 0;
    • 当当前遍历结点 ( i , j ) (i, j) (i,j) 为陆地时,根节点数组 p a r e n t parent parent 初始值为 i ∗ c o l s + j i*cols + j icols+j,代表以每一个陆地 ( i , j ) (i,j) (i,j) 初始化一棵树。
  • n u m I s l a n d s numIslands numIslands 方法中,只要出现陆地,就将该陆地周围的陆地 u n i o n union union 起来。如下所示 —>
    【并查集】B001_岛屿数量(并查集)_第1张图片
  • 最后返回的是 U F UF UF 连通分量的个数 c o u n t count 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) O(MN),其中 M M M N N N 分别为网格 g r i d grid grid 行数和列数。
  • 空间复杂度: O ( 2 ∗ M ∗ N ) O(2*M*N) O(2MN) p a r e n t 、 r a n k parent、rank parentrank 数组花费主要的空间。

你可能感兴趣的:(#,并查集)