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 解释: 无需交换座位,所有的情侣都已经可以手牵手了。
解题思路
这一题只是求交换座位的次数:2对情侣做错位置时,需要交换的次数为1次,3对情侣做错位置时,需要交换的次数为2次,所以交换次数最少的为做错位置情侣对数-1次。
因为每队情侣的对号是一定的,如0,1是第0队,2,3是第1队...
那么如果在做错位置的情况下
[1,2] [0,3] [4,5]
需要调整的对号是0,1两队,所以是0,1两对做交换
我们可以把需要做交换的对号做成关联分组,然后每组需要交换的次数是这一组情侣对的数量-1
假设一共有N对情侣
那么N1+N2+N3+...+Nn = N
分组为
S1 + S2 + S3 = N
那么需要交换的次数为 S1-1 + S2-1 + S3-1 +...+ Sm-1 = N - m
m为关联分组的大小
代码
/**
* @param {number[]} row
* @return {number}
*/
class UnionFind {
constructor(n) {
this.parent = new Array(n).fill(0).map((v, index) => index)
this.rank = new Array(n).fill(1)
this.count = n
}
getCount() {
return this.count
}
connected(a, b) {
return this.find(a) == this.find(b)
}
find(x) {
if (this.parent[x] != x) {
this.parent[x] = this.find(this.parent[x])
}
return this.parent[x]
}
merge(a, b) {
let ra = this.find(a), rb = this.find(b)
if (ra == rb) return
if (this.rank[ra] < this.rank[rb]) {
this.parent[ra] = rb
this.rank[rb] += this.rank[ra]
} else {
this.parent[rb] = ra
this.rank[ra] += this.rank[rb]
}
this.count--
}
}
var minSwapsCouples = function(row) {
const len = row.length
const uf = new UnionFind(len / 2)
for (let i = 0; i < len; i+=2) {
uf.merge(row[i] / 2 | 0, row[i + 1] / 2 | 0)
}
return len / 2 - uf.getCount()
};