【路飞】并查集

148 阅读1分钟

介绍

并查集又称Union-Find,属于图的一种,是一种很不一样的树结构

应用

  1. 处理连接问题,比如网络(抽象的概念,不特指因特网)间节点的连接状态;
  2. 数学中的集合类实现,比如求两个集合中的并集;

实现

详细分析请看这篇文章:labuladong.gitee.io/algo/2/19/3…

class UF {
  count = 0;
  size = [];
  parent = [];
  // n 为图中节点的个数
  constructor(n) {
    this.count = n;
    this.parent = [];
    this.size = [];
    for (let i = 0; i < n; i++) {
      this.parent[i] = i;
      this.size[i] = 1;
    }
  }
  // 将节点 x 和节点 y 连通
  union(x, y) {
    const xr = this.find(x);
    const yr = this.find(y);
    if (xr === yr) return;
    // 小树接到大树下面,使树整体平衡
    if (this.size[xr] < this.size[yr]) {
      this.parent[xr] = yr;
      this.size[yr] += this.size[xr];
    } else {
      this.parent[yr] = xr;
      this.size[xr] += this.size[yr];
    }
    this.count--;
  }

  // 判断节点 x 和节点 y 是否连通
  isConnected = (x, y) => this.find(x) === this.find(y);

  // 返回节点 x 的根节点
  find(x) {
    while (x !== this.parent[x]) {
      this.parent[x] = this.parent[this.parent[x]];
      x = this.parent[x];
    }
    return x;
  }

  // 返回图中的连通分量个数
  count() {
    return this.count;
  }
}

解析

如下图所示,共有10个节点,连通分量为 10 个;
第一次,调用union(0,1),连通0和1,连通分量降为 9 个;
第二次,调用union(1,2),连通1和2,连通分量变为 8 个;
这时调用 isConnected(0,2)返回true

连通的性质:
1、自反性:节点 x 和 y 是连通的;
2、对称性:如果节点 x 和 y 连通,那么 y 和 x 也连通;
3、传递性:如果节点 x 和 y 连通,y 和 z 连通,那么 x 和 z 也连通。

image.png

力扣刷题

题目:990. 等式方程的可满足性

image.png

解答

先构建连通性,然后再处理!=是否破坏了连通性;

function equationsPossible(equations) {
  const uf = new UF(26);
  const codeA = "a".charCodeAt(0);
  // 构建连通性
  for (let i = 0; i < equations.length; i++) {
    const item = equations[i];
    if (item[1] === "=") {
      uf.union(item[0].charCodeAt(0) - codeA, item[3].charCodeAt(0) - codeA);
    }
  }
  // 处理!=是否破坏了连通性
  for (let i = 0; i < equations.length; i++) {
    const item = equations[i];
    if (item[1] === "!") {
      if (
        uf.isConnected(
          item[0].charCodeAt(0) - codeA,
          item[3].charCodeAt(0) - codeA
        )
      ) {
        return false;
      }
    }
  }
  return true;
}

class UF {
  count = 0;
  size = [];
  parent = [];
  constructor(n) {
    this.count = n;
    this.parent = [];
    this.size = [];
    for (let i = 0; i < n; i++) {
      this.parent[i] = i;
      this.size[i] = 1;
    }
  }

  union(x, y) {
    const xr = this.find(x);
    const yr = this.find(y);
    if (xr === yr) return;
    // 小树接到大树下面,较平衡
    if (this.size[xr] < this.size[yr]) {
      this.parent[xr] = yr;
      this.size[yr] += this.size[xr];
    } else {
      this.parent[yr] = xr;
      this.size[xr] += this.size[yr];
    }
    this.count--;
  }

  isConnected = (x, y) => this.find(x) === this.find(y);

  find(x) {
    while (x !== this.parent[x]) {
      this.parent[x] = this.parent[this.parent[x]];
      x = this.parent[x];
    }
    return x;
  }

  count() {
    return this.count;
  }
}