【题解】力扣765. 情侣牵手

765. 情侣牵手

题目来源

765. 情侣牵手

思路

如果一对情侣恰好坐在了一起,并且坐在了成组的座位上,其中一个下标一定是偶数,另一个一定是奇数,并且「偶数的值 + 1 = 奇数的值」。例如编号数对 [2, 3][9, 8],这些数对的特点是除以 2(下取整)得到的数相等。

方法一、并查集

将N对情侣看做图中的\(N\)个节点;对于没对相邻的位置,如果第\(i\)对和第\(j\)对坐在了一起,则在 \(i\)号节点与 \(j\) 号节点之间连接一条边,代表需要交换这两对情侣的位置。

如果途中形成了一个大小为k的环:\(i\to j\to k\to ... \to l \to i\),则我们沿着环的方向,先交换\(i,j\)的位置,在交换\(j,k\)的位置,一次类推。在进行了\(k-1\)次交换后,这\(k\)对情侣就能够彼此牵手了。

故我们只需要利用并查集求出图中的每个连通分量;对于每个连通分量而言,其大小减 \(1\) 就是需要交换的次数。

我们最终需要的是N对情侣全部分开,所以 “交换之后的连通分量”就是N无疑,交换之前的连通分量就是我们\(uf.getCount()\), 假设为\(m\),即交换之前有m个连通分量。其中对于每个连通分量,假设各自的成员数(情侣对数)分别为 \(c_1, c_2, c_3......c_m\)。对于每一个连通分量,其最少交换次数都是\(n - 1\)次(都是循环交换),因此总最少交换次数就是$ (c_1 - 1) + (c_2 - 1) + (c_3 - 1) +......+ (c_m - 1)$。括号拆出来,再合并一下就变成了 \((c_1 + c_2 + c_3 +....+ c_m) - (1 + 1 + 1 +...... + 1)\),其中左边括号加起来就相当于是总情侣对数\(N\), 右边共有\(m\)个,如此最后最少交换次数就应该是 \(N - m\) 了。

代码

public class Solution {
    public int minSwapsCouples(int[] row) {
        int len = row.length;
        int N = len / 2; // 所有情侣的数量
        UnionFind unionFind = new UnionFind(N); // 初始化
        for (int i = 0; i < row.length; i += 2) {
            unionFind.union(row[i] / 2, row[i + 1] / 2);
        }
        return N - unionFind.getCount();

    }

    /**
     * UnionFind并查集类
     */
    public class UnionFind {

        int[] parent;
        int count; // 并查集里连通分量的个数(交换之前的连通分量)

        int getCount() {
            return count;
        }
        /*
        按秩合并:
        int[] rank;

        初始化时:
        在for循环里面:rank[i] = 1;

        合并方法里:
        int rootx = find(x);
        int rooty = find(y);
        if(rank[rootx] <= rank[rooty]) 
            p[rootx] = rooty;
        else
            p[rooty] = rootx;
        if(rank[rootx] == rank[rooty] && rootx != rooty)
            rank[rooty]++;  //如果深度相同且根节点不同,则新的根节点的深度+1
        */

        // 初始化
        UnionFind(int n) {
            count = n;
            parent = new int[n];
            for (int i = 0; i < n; i++) {
                parent[i] = i;
            }
        }

        // 查找
        public int find(int x) {
            while (x != parent[x]) {
                parent[x] = parent[parent[x]];
                x = parent[x];
            }
            return x;
            /*
            return p[x] == x ? p[x] : (p[x] = find(p[x]);   // 路径压缩:p[x] = find(p[x]);
            */
        }

        // 合并
        public void union(int x, int y) {
            int rootx = find(x);
            int rooty = find(y);
            if (rootx == rooty) { // 是一对情侣
                return;
            }

            parent[rootx] = rooty;  // 合并
            count--;

            /*
            p[find(x)] = find(y);
            */

        }

    }
}

参考来源

  1. Pecco
  2. 力扣官方题解.🎦 情侣牵手
posted @ 2021-03-08 16:24  zzzzzy2k  阅读(88)  评论(0编辑  收藏  举报