返回

并查集的奇妙世界——JS中的算法魅力

前端

前言

算法的世界中,有一种数据结构叫“并查集”,它以其简洁的结构和高效的算法在各种领域大放异彩。今天,我们就来以leetcode中的“岛屿的最大面积”问题为例,一起探究并查集的魅力。

并查集简介

并查集是一种数据结构,可以维护一组元素的集合。它的主要目的是检测两个元素是否属于同一个集合,以及将两个集合合并为一个。并查集有两种基本操作:查找和合并。

  • 查找: 查找操作确定一个元素属于哪个集合。
  • 合并: 合并操作将两个集合合并为一个集合。

并查集通常使用数组来实现。每个元素在数组中的位置就是其集合的ID。查找操作只需要返回元素所在位置的值即可。合并操作则需要将两个元素所在集合的ID值更新为同一个值,以便将它们合并为一个集合。

并查集在“岛屿的最大面积”问题中的应用

leetcode中的“岛屿的最大面积”问题是这样的:给你一个大小为 m x n 的二进制矩阵 grid 。岛屿是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。返回 grid 中最大的岛屿的面积。

/**
 * 给定一个大小为 m x n 的二进制矩阵 grid 。
 * 岛屿是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。
 * 返回 grid 中最大的岛屿的面积。
 *
 * 示例 1:
 * 输入:grid = [[0,0,1,0,0,0,0,1,0,0,0],[0,0,0,0,0,0,0,1,1,1,0],[0,1,1,0,1,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,0],[0,0,0,0,0,0,0,0,0,0,0]]
 * 输出:6
 *
 * 示例 2:
 * 输入:grid = [[0,0,0,0,0,0,0,0]]
 * 输出:0
 *
 * 提示:
 * m == grid.length
 * n == grid[i].length
 * 1 <= m, n <= 500
 * grid[i][j] 为 0 或 1
 */
const maxAreaOfIsland = (grid) => {
  // 定义并查集
  const uf = new UnionFind(grid);

  // 遍历网格
  for (let i = 0; i < grid.length; i++) {
    for (let j = 0; j < grid[0].length; j++) {
      // 如果当前单元格为陆地
      if (grid[i][j] === 1) {
        // 与上下左右的陆地合并
        if (i > 0 && grid[i - 1][j] === 1) {
          uf.union(i * grid[0].length + j, (i - 1) * grid[0].length + j);
        }
        if (j > 0 && grid[i][j - 1] === 1) {
          uf.union(i * grid[0].length + j, i * grid[0].length + j - 1);
        }
      }
    }
  }

  // 找到最大的岛屿面积
  let maxArea = 0;
  for (let i = 0; i < grid.length; i++) {
    for (let j = 0; j < grid[0].length; j++) {
      // 如果当前单元格为陆地
      if (grid[i][j] === 1) {
        // 获取当前单元格所属集合的面积
        const area = uf.getSize(i * grid[0].length + j);
        maxArea = Math.max(maxArea, area);
      }
    }
  }

  return maxArea;
};

// 并查集类
class UnionFind {
  constructor(grid) {
    this.parent = [];
    this.size = [];
    // 初始化并查集
    for (let i = 0; i < grid.length * grid[0].length; i++) {
      this.parent[i] = i;
      this.size[i] = 1;
    }
  }

  // 查找元素的根节点
  find(x) {
    if (this.parent[x] !== x) {
      this.parent[x] = this.find(this.parent[x]);
    }
    return this.parent[x];
  }

  // 合并两个集合
  union(x, y) {
    const rootX = this.find(x);
    const rootY = this.find(y);
    if (rootX === rootY) {
      return;
    }
    // 将较小集合的根节点指向较大集合的根节点
    if (this.size[rootX] < this.size[rootY]) {
      this.parent[rootX] = rootY;
      this.size[rootY] += this.size[rootX];
    } else {
      this.parent[rootY] = rootX;
      this.size[rootX] += this.size[rootY];
    }
  }

  // 获取集合的面积
  getSize(x) {
    return this.size[this.find(x)];
  }
}

结语

通过“岛屿的最大面积”问题,我们领略了并查集的魅力。并查集是一种非常有用的数据结构,它可以高效地维护一组元素的集合,并支持查找和合并操作。在实际应用中,并查集可以解决各种各样的问题,例如:

  • 社交网络中的连通性问题
  • 图论中的最小生成树问题
  • 图论中的最短路径问题
  • 数据压缩问题

希望这篇文章能激发您对算法和数据结构的兴趣,并鼓励您在自己的项目中使用它们。如果您有任何问题或建议,欢迎在评论区留言。