不管全世界所有人怎么说,我都认为自己的感受才是正确的。无论别人怎么看,我绝不打乱自己的节奏。喜欢的事自然可以坚持,不喜欢的怎么也长久不了。
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
解释: 无需交换座位,所有的情侣都已经可以手牵手了。
说明:
len(row)是偶数且数值在[4, 60]范围内。- 可以保证
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;
}
}