[路飞] 连通网络的操作次数

129 阅读2分钟

题目描述

用以太网线缆将 n 台计算机连接成一个网络,计算机的编号从 0 到 n-1。线缆用 connections 表示,其中 connections[i] = [a, b] 连接了计算机 a 和 b。

网络中的任何一台计算机都可以通过网络直接或者间接访问同一个网络中其他任意一台计算机。

给你这个计算机网络的初始布线 connections,你可以拔开任意两台直连计算机之间的线缆,并用它连接一对未直连的计算机。请你计算并返回使所有计算机都连通所需的最少操作次数。如果不可能,则返回 -1 。

解题思路

算法,数据结构

并查集

可行性判断

connections,即线缆数量一定是 n - 1,也就是说线缆的数量一定要是 电脑数量(n) -1,因为题目明确说明了,没有多余

思路

连接起来的电脑,包括单独未被连接的电脑,是不同的集合
比如 1,2,3,4 连在一起,5,6两台没人连,那么就有 3 个集合,
我们要做的是,先把 1,2,3,4 连接起来,此时一定会消耗 3 个线缆,
那么剩下的线缆是否刚好够用,也就是剩 2 条把 5,6 和 1,2,3,4能连接在一起 将这 3 个集合连接起来需要 2 个线缆

因此得出结论,我们需要多余的线缆数量,是 集合数量(cnt) - 1

所以我们先完成布线,然后看有多少个集合,最后 集合数量 - 1 就是返回值

将初始布线实现

这是为了后续计算有多少个集合,把没连接的集合连接在一起需要多少线缆

我们遍历 connections,执行 merge,依次连接两台电脑

看有哪些没连接,返回集合数量 - 1

遍历并查集,看有多少个没连接的(并查集中和自己相连的),每个没连接的就是一个集合集合,那么需要的线缆是 集合数量(cnt) - 1

代码

/**
 * @param {number} n
 * @param {number[][]} connections
 * @return {number}
 */
var makeConnected = function (n, connections) {
  if (connections.length < n - 1) return -1
  const u = new UnionSet(n)
  let cnt = 0

  // connect
  for (const item of connections) {
    const node1 = item[0]
    const node2 = item[1]

    u.merge(node1, node2)
  }

  // find disconnect numbers
  for (let i = 0; i < n; i++) {
    if (u.get(i) === i) cnt++
  }

  return cnt - 1
}

class UnionSet {
  constructor(n) {
    this.fa = []
    for (let i = 0; i < n; i++) {
      this.fa[i] = i
    }
  }

  get(x) {
    return (this.fa[x] = this.fa[x] === x ? x : this.get(this.fa[x]))
  }

  merge(a, b) {
    this.fa[this.get(b)] = this.get(a)
  }
}