大家好,我是挨打的阿木木,爱好算法的前端摸鱼老。最近会频繁给大家分享我刷算法题过程中的思路和心得。如果你也是想提高逼格的摸鱼老,欢迎关注我,一起学习。
题目
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
解释: 无需交换座位,所有的情侣都已经可以手牵手了。
说明:
len(row)是偶数且数值在[4, 60]范围内。- 可以保证
row是序列0...len(row)-1的一个全排列。
思路
- 这道题目实现很简单,难的是思维的转换。比如说本来
1和2,3和4应该牵手的,但是现在的座位是1和3,2和4,我们看一眼都知道换一次位置就可以了。但是如何让代码世界里的元素知道这个事情呢? - 我们可以建立
1和3,2和4的链接关系。我们会发现只要1和2牵手了,那么3和4自然也是牵手的了; - 这种场景在我们算法时间中叫做并查集,我们先经过一轮遍历,建立好初始化时两两之间的链接关系;
- 然后通过第二轮遍历,把相邻两者牵线起来,如果他们两个签完线后,有两个另外的人也通过他们串接起来,那么说明这两者经过一轮交换座位刚好能满足双方都达到牵手的目的;
- 统计完我们的牵线次数直接返回即可。
实现
/**
* @param {number[]} row
* @return {number}
*/
var minSwapsCouples = function(row) {
const n = row.length;
const uf = new UnionFind(n);
const mid = n / 2;
// 先连通当前的值
for (let i = 0; i < mid; i++) {
uf.merge(row[2 * i], row[2 * i + 1]);
}
let count = 0;
// 再看看多少个没连通的,串起来
for (let i = 0; i < mid; i++) {
let index1 = uf.find(2 * i), index2 = uf.find(2 * i + 1);
// 如果相邻位置的还没联通,串起来
if (index1 !== index2) {
uf.merge(index1, index2);
count++;
}
}
return count;
};
class UnionFind {
constructor(n) {
// 一开始每个元素的父元素都是自己
this.parent = new Array(n).fill(0).map((item, index) => index);
}
// 找到元素的父元素
find(index) {
return this.parent[index] = this.parent[index] === index ? index : this.find(this.parent[index]);
}
// 把index2的父元素设置为index1的父元素
merge(index1, index2) {
this.parent[this.find(index2)] = this.find(index1);
}
}
看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。