LeetCode 947. 移除最多的同行或同列石头 并查集

传送门

思路

干货太干就不太好理解了,以下会有点话痨( ̄▽ ̄)"

首先题目给了一个二维stones数组,存储每个石子的坐标,因为在同行或者同列的石子最终可以被取到只剩下一个,那么我们将同行同列的石子归属于一个集合,开始套用并查集的思想求一下有几个集合似乎就搞定了?想到这是我看到题目的想法,但是为了实际使用并查集,有几个细节需要注意:

  • 本题并查集究竟要合并什么?对于一个坐标(x,y)来说它有两个值组成,x表示所属x行,y表示所属于y列,而按照我们既定的思维,同行同列的石子属于一个集合,那么关键就在于(x,y)作为桥梁沟通了x行和y列,使得行集合与列集合合并成一个集合了(它们两个集合可以取得只剩下一个石子),所以需要合并的是行号和列号

  • 这个stones数组存储的是二维空间的坐标,而并查集是在一维进行操作的,好比说0行0列都是0,怎么区分?这里用一个朴素的方法,由于x,y的取值范围为0~10^4,那么将其中一个轴+10001或者减去10001就能保证二维的数据映射到一维进行区分,就是x映射为x+10001,用映射的值去和y进行并查集操作

  • 最后我们统计一个所有的不相交子集的个数(好像也叫做:极大连通子图个数,连通分量),用石子数减去集合数就是最多可以移除的石子数

Java解法,其实是伪装成Java的c++,由于还不怎么熟悉Java的一些集合框架,这题直接开了大的空间

class Solution {
    int parent[] = new int[20005];
    int vis[] = new int[20005];

    public int removeStones(int[][] stones) {
        int ans = 0;
        for (int i = 0; i < 20005; i++){
            parent[i] = i;
            vis[i] = 0;
        }
        for (int[] stone : stones) {
            //(x,y)是一个坐标,而第x行所有点在一个集合,第y列
            //所有点在一个集合,(x,y)的存在就使得x行集合与y列
            //集合可以合并为一个集合,因为它们构成了一个连通图
            //配合+10001操作,并查集可以从二维转化为一维,从而
            //实现编号为x+10001的行集合与编号为y的列集合的合并
            union(stone[0] + 10001, stone[1]);
            vis[stone[0] + 10001] = 1;
            vis[stone[1]] = 1;
        }
        for(int i = 0; i < 20005; i++) {
            if(vis[i] == 1 && parent[i] == i) ans++;
        }
        return stones.length - ans;
    }

    public void union(int x, int y) {
        int fx = find(x);
        int fy = find(y);
        if (fx != fy) parent[fy] = fx;
    }

    public int find(int x) {
        while (parent[x] != x) x = parent[x];
        return x;
    }
}

posted on 2021-01-17 14:38  白泽talk  阅读(138)  评论(0编辑  收藏  举报