题目
765. 情侣牵手
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
解释: 无需交换座位,所有的情侣都已经可以手牵手了。
解法一
思路
将情侣按目前座位的分布,将同一座位上的情侣,应该所属的座位连接在一起
假设有(0,3,2,1),两对情侣,分别是(0,3)坐在0号座位,(2,1)坐在1号座位
0应该属于0号座位,3应该属于1号座位,那么我们将(0,3)两个情侣应该属于的逻辑正确座位0,1相连,形成一个连通分量,那么这个连通分量里就有两对情侣
2应该属于1号座位,1应该属于0号座位,上一步0,1号座位已经连接了,这里就可以跳过
然后,一个连通分量里的情侣,需要交换多少次才能让所有情侣牵手呢
假设有2对,那么需要交换1次 假设有3对,那么需要交换2次 假设有4对,那么需要交换3次 ... 假设有N对,那么需要交换N-1次
那么有朋友会问了,(0,2,1,3,4,6,5,7),这种情况,明明是4对情侣,却只用交换2次
即2 <=> 1, 6 <=> 5就可以完成,那是为什么呢?
那么我们仔细分析一下,其实这种情况下,看似4对情侣都未牵手,但实际里面有两个连通分量
分别为(0,2,1,3)和(4,6,5,7),两个连通分量内分别都都是2对情侣,各需要1次交换才能完成
所有仍然是符合我们上面总结的,假设1个连通分量里有N对情侣,需要交换N-1次的逻辑
那么假设我们有n个连通分量,每个连通分量里的情侣分别为N1,N2,N3,N4....Nn
那么即可得到 总交换次数 = (N1-1)+(N2-1)+(N3-1)+(N4-1)+...+(Nn-1)
又由于N1,N2,N3,N4....Nn代表的是每个连通分量里的情侣对数,
那么N1+N2+N3+N4+...+Nn 肯定等于 总情侣对数N
所以上面等式我们转换以下即可得到 (N1-1)+(N2-1)+(N3-1)+(N4-1)+...+(Nn-1) = N1+N2+N3+N4+...+Nn -n = N -n
即总交换次数 = 总情侣对数 - 连通分量个数
这里我仍然再强调一下,我们是将实际坐在一起的情侣所属的逻辑正确的座位合并在一个连通分量里
即(0,5), 0属于0号座位,5属于 Math.floor(5/2) = 2号座位
我们是将0号座位和2号座位连在了一起
代码如下
/**
* @param {number[]} row
* @return {number}
*/
var minSwapsCouples = function(row) {
let len = row.length;
let N = len / 2;
let uf = new UnionFind(N);
for (let i = 0; i < len; i += 2) {
uf.union(Math.floor(row[i]/2), Math.floor(row[i+1]/2));
}
return N - uf.size();
};
class UnionFind{
constructor(size){
this.parent = Array(size).fill(0).map((el,i)=>i);
this.count = size;
}
size(){
return this.count;
}
find(x){
if(this.parent[x] != x){
this.parent[x] = this.find(this.parent[x])
}
return this.parent[x]
}
union(p,q){
let rootP = this.find(p)
let rootQ = this.find(q)
if(rootP == rootQ){
return;
}
this.parent[rootP] = this.parent[rootQ]
this.count--;
}
}