「这是我参与2022首次更文挑战的第38天,活动详情查看:2022首次更文挑战」
题目介绍
n 对情侣坐在连续排列的 2n 个座位上,想要牵到对方的手。
人和座位由一个整数数组 row 表示,其中 row[i] 是坐在第 i 个座位上的人的 ID。情侣们按顺序编号,第一对是 (0, 1),第二对是 (2, 3),以此类推,最后一对是 (2n-2, 2n-1)。
返回 最少交换座位的次数,以便每对情侣可以并肩坐在一起。 每次交换可选择任意两人,让他们站起来交换座位。
示例1
输入: row = [0,2,1,3]
输出: 1
解释: 只需要交换row[1]和row[2]的位置即可。
示例2
输入: row = [3,2,0,1]
输出: 0
解释: 无需交换座位,所有的情侣都已经可以手牵手了。
提示:
2n == row.length2 <= n <= 30n是偶数0 <= row[i] < 2nrow中所有元素均无重复
解题思路
- 首先如果是
两对情侣坐错位置,那么将他们换到正确的位置上需要1次 - 如果是
三对情侣之间彼此坐错位置,那么将他们换到正确的位置上至少需要2次
- 如果是
四对情侣之间彼此坐错位置,那么将他们换到正确的位置上至少需要3次
因此,如果有 n 对情侣彼此坐错位置的话,最少需要交换的次数为 n - 1
如果利用并查集来做的话,首先将情侣按对来编号,将彼此之间坐错位置的情侣连通起来,最后会得到 m 个集合,在这 m 个集合中,每个集合中的情侣只是和集合中的其他情侣坐错位置,和其他集合中的情侣没有关系,如果要将每个集合中的情侣正确拼对的话,需要的次数为 集合中的情侣对数 - 1,那么将 m 个集合中的情侣全部正确拼对的话,就需要 初始情侣对数 - 集合个数
解题代码
var minSwapsCouples = function(row) {
// 计算情侣对数
const n = row.length / 2
const unionSet = new UnionSet(n)
for (let i = 0; i < row.length; i += 2) {
// 每队情侣的编号为 0: (0, 1), 1: (2, 3) …… i: (2i, 2i + 1)
unionSet.merge(Math.floor(row[i] / 2), Math.floor(row[i + 1] / 2))
}
// 情侣对数 - 集合数
return n - unionSet.getCount()
};
// 并查集
class UnionSet {
constructor(n) {
this.fa = []
this.size = []
// 初始时每队情侣为一个集合,有 n 个集合
this.count = n
for (let i = 0; i < n; i++) {
this.fa[i] = i
this.size[i] = 1
}
}
get(v) {
if (this.fa[v] === v) return v
const root = this.get(this.fa[v])
this.fa[v] = root
return root
}
merge(a, b) {
const ra = this.get(a), rb = this.get(b)
if (ra === rb) return
if (this.size[ra] < this.size[rb]) {
this.fa[ra] = rb
this.size[rb] += this.size[ra]
} else {
this.fa[rb] = ra
this.size[ra] += this.size[rb]
}
// 每两个集合进行连通,则集合的数量少 1
this.count--
}
// 获取集合数量
getCount() {
return this.count
}
}