4 分钟阅读

困难算法题,按公因数计算最大组件大小,使用 JavaScript 语言编写

题目

给定一个由不同正整数组成的非空数组 nums,考虑下面的构图:

  • 有 nums.length 个节点,按照从 nums[0] 到 nums[nums.length-1]标记;
  • 只有当 nums[i] 和 nums[j] 共用一个大于 1 的公因数时,nums[i] 和 nums[j] 之间才有一条边。

返回构图中最大连通组件的大小。

输入格式

输入为数组元素,空格分隔

输出格式

输出最大连通组件的大小

输入样例

4 6 15 35

输出样例

4

分析

解答


// 这是题目要求的输入格式
let inputs = `
  4 6 15 35
`;
const nums = inputs.trim().split(' ').map(Number);

// 查找最大公约数
function findGCD(a, b) {
  while (b !== 0) {
    [a, b] = [b, a % b];
  }
  return a;
}

/**
    [0, 1, 2, ... , size]   // 初始状态下没有连通的节点,所有节点的父节点都是自身
    union(4, 6) // 把4跟6连通起来
    [..., 4, 5, 4, 7, 8, ... , size] // parent[6]设成parent[4],表示两个节点共用了一个父节点,也就是连通了
    union(6, 15);
    [..., 4, 5, 4, 7, 8, ... , 13, 14, 4, 16, ... , size] // 15跟6也连通了,这时候componentSize就达到3了
*/
// 并查集
class UnionFind {
  constructor(size) {
    // 初始化 parent 数组,每个节点的父节点初始化为自己
    this.parents = Array.from({ length: size }, (_, i) => i);
    // 根节点连通量
    this.componentSize = new Array(size).fill(1);
  }
  find(x) {
    if (this.parents[x] !== x) {
      return this.find(this.parents[x]);
    }
    return x;
  }
  union(x, y) {
    const rootX = this.find(x);
    const rootY = this.find(y);
    if (rootX !== rootY) {
      this.parents[rootY] = rootX;
      this.componentSize[rootX] += this.componentSize[rootY];
    }
  }
  getLargestSize() {
    return Math.max(...this.componentSize);
  }
}
function largestComponentSize(nums) {
  // 确保parents能装下所有数字
  const uf = new UnionFind(Math.max(...nums) + 1);
  for (let i = 0; i < nums.length; i++) {
    for (let j = 1; j < nums.length; j++) {
      if (findGCD(nums[i], nums[j]) > 1) {
        uf.union(i, j);
      }
    }
  }
  return uf.getLargestSize();
}

// 示例
console.log(largestComponentSize(nums)); // 输出 4

留下评论