765. 情侣牵手 | 并查集

18 阅读2分钟

题目描述:

n 对情侣坐在连续排列的 2n 个座位上,想要牵到对方的手。

人和座位由一个整数数组 row 表示,其中 row[i] 是坐在第 i 个座位上的人的 ID。情侣们按顺序编号,第一对是 (0, 1),第二对是 (2, 3),以此类推,最后一对是 (2n-2, 2n-1)

返回 最少交换座位的次数,以便每对情侣可以并肩坐在一起。 每次交换可选择任意两人,让他们站起来交换座位

我们可以给每一对情侣编号,编号为 idx 的人对应的 “情侣编号”idx / 2;

如果有 k 对情侣相互之间坐错了位置,也就是说,这 k 对情侣仅通过互相之间交换位置就能够满足牵手,那么他们之间需要且仅需要经过 k−1 次交换来坐到正确的位置上。这样的 k 对情侣形成了一个 “环”。

为什么?不妨这样考虑,我们先调整一对情侣的位置,使其坐到正确的位置上,那么问题就从 k 对情侣的问题,转换成了 k−1 对情侣的问题。依此类推,最终 k=1 时,交换次数为 0。所以,如果 k 对情侣相互之间坐错了位置,那么需要 k−1 次交换。

因此,我们只需要遍历一遍数组,利用并查集找出有多少个环。假设有 x 个,每个环中分别有 y1,y2,,yx(1<=yi<=n)y_1,y_2,⋯,y_x(1 <= y_i <= n) 对情侣,那么答案就是 y11+y21++yx1=y1+y2++yxx=nxy_1−1+y_2−1+⋯+y_x−1=y_1+y_2+⋯+y_x−x=n−x

class Solution {
    private int[] fa;

    private int find(int x) {
        if(fa[x] != x) {
            fa[x] = find(fa[x]);
        }
        return fa[x];
    }

    public int minSwapsCouples(int[] row) {
        // 共 n 对情侣
        int n = row.length >> 1;
        fa = new int[n];
        for(int i = 0; i < n; i++) fa[i] = i;
        // 把相邻的两个人放到一个并查集中
        for(int i = 0; i < n << 1; i += 2) {
            int a = row[i] >> 1, b = row[i + 1] >> 1;
            fa[find(a)] = find(b);
        }
        int ans = n;
        // 每有一个集合/环,答案减一
        for(int i = 0; i < n; i++) {
            if(i == fa[i]) ans--;
        }
        return ans;
    }
}