【路飞】算法与数据结构-情侣牵手

127 阅读2分钟

不管全世界所有人怎么说,我都认为自己的感受才是正确的。无论别人怎么看,我绝不打乱自己的节奏。喜欢的事自然可以坚持,不喜欢的怎么也长久不了。

LeetCode:原题地址

题目要求

N 对情侣坐在连续排列的 2N 个座位上,想要牵到对方的手。 计算最少交换座位的次数,以便每对情侣可以并肩坐在一起。 一次交换可选择任意两人,让他们站起来交换座位。

人和座位用 0 到 2N-1 的整数表示,情侣们按顺序编号,第一对是 (0, 1),第二对是 (2, 3),以此类推,最后一对是 (2N-2, 2N-1)。

这些情侣的初始座位  row[i] 是由最初始坐在第 i 个座位上的人决定的。

示例 1:

输入: row = [0, 2, 1, 3]
输出: 1
解释: 我们只需要交换row[1]和row[2]的位置即可。

示例 2:

输入: row = [3, 2, 0, 1]
输出: 0
解释: 无需交换座位,所有的情侣都已经可以手牵手了。

说明:

  1. len(row) 是偶数且数值在 [4, 60]范围内。
  2. 可以保证row 是序列 0...len(row)-1 的一个全排列。

思路

遍历row数组,每次都取出来两个人,默认情况下情侣的编号是从0开始的 一对正确的情侣就是一个偶数和一个奇数;并且 奇数 = 偶数+1;以及 奇数/2 == 偶数/2;符合这样的条件才是正确的一对情侣 否则,就需要将两个编号除以2之后的结果进行连通,进行交换次数。 当n-1 对配对成功的时候,最后一对也会配对成功,我们只需要记录连通合并的次数。这就是需要调整的次数。

/**
 * @param {number[]} row
 * @return {number}
 */
var minSwapsCouples = function (row) {
  let len = row.length;
  let N = len >> 1;// 右移一位,相当于除以2
  let uf = new UnionFind(N);
  for (let i = 0; i < len; i += 2) {// 跳着循环,每次取两个值
    uf.unite(row[i] >> 1, row[i + 1] >> 1);// 拿到这两个人之后,都给除以2,进行连通
  }

  return N - uf.getCount();
};

// 并查集的模板
class UnionFind {
  constructor(n) {
    this.parent = new Array(n).fill(0).map((v, i) => i);
    this.rank = new Array(n).fill(1);
    this.setCount = n; // 连通分量
  }
  findSet (index) {
    if (this.parent[index] !== index) {
      this.parent[index] = this.findSet(this.parent[index]);
    }
    return this.parent[index];
  }
  unite (index1, index2) { // 合并
    // 拿到他们的顶点坐标
    let root1 = this.findSet(index1), root2 = this.findSet(index2);
    if (root1 !== root2) {
      // 判断一下各自集合的节点个数,节点少的集合要往节点多的集合上合并
      if (root1 < root2) {
        [root1, root2] = [root2, root1]; // root1 是节点个数多的集合
      }
      // 合并根节点
      this.parent[root2] = root1;
      // 计算已经合并的节点数量
      this.rank[index1] += this.root2;
      this.setCount--; // 合并一个,城市数量减去一
    }
  }
  getCount () {
    return this.setCount;
  }
  connected (index1, index2) { // 来判断两个顶点,是否是一个连通分量
    let root1 = this.findSet(index1), root2 = this.findSet(index2);
    return root1 === root2;
  }
}