[路飞]_leetcode_765. 情侣牵手

147 阅读2分钟

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()
};