Most Stones Removed

Given a matrix of integers A of size N x 2 describing coordinates of N stones placed in 2D plane.
Now, a move consists of removing a stone that shares a column or row with another stone on the plane.

Find and return the largest number of moves you can make.

Note: Each of the N coordinate points contains exactly one stone.



Input Format

The first argument given is the integer matrix A.

Output Format

Return the largest number of moves you can make.

Constraints

1 <= N <= 100000
0 <= A[i][0], A[i][1] <= 10000

For Example

Input 1:
    A = [   [0, 0]
            [0, 1]
            [1, 0]
            [1, 2]
            [2, 2]
            [2, 1]   ]
Output 1:
    5
Explanation 1:
    One of the order of removing stones:
    1. Remove (2,1) as it shares row with (2,2)
        remaining stones ( (0,0), (0,1), (1,0), (1,2) and (2,2)).
    2. Remove (2,2) as it shares column with (1,2)
        remaining stones ( (0,0), (0,1), (1,0) and (1,2)).
    3. Remove (0,1) as it shares row with (0,0)
        remaining stones ( (0,0), (1,0) and (1,2)).
    4. Remove (1,2) as it shares row with (1,0)
        remaining stones ( (0,0) and (1,0)).
    5. Remove (0,0) as it shares column with (1,0)
        remaining stones ((1,0)).
   So the maximum number of moves is 5.
    
Input 2:
    A = [   [0, 0]
            [0, 2]
            [2, 0]
            [1, 1]
            [2, 2]   ]
Output 2:
    3
Solution:

Time: O(n^2)

public class Solution {
    static class UF {
        private int count;
        private int[] parent;
        private int[] size;
        
        public UF(int n) {
            this.count = n;
            parent = new int[n];
            size = new int[n];
            for (int i = 0; i < n; i ++) {
                parent[i] = i;
                size[i] = 1;
            }
        }
        
        public int find(int x) {
            while (x != parent[x]) {
                parent[x] = parent[parent[x]];
                x = parent[x];
            }
            return x;
        }
        
        public int[] size() { return size; }
        public int[] parent() { return parent; }

        public void union(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);
            if (rootX == rootY) {
                return;
            }
            if (size[rootX] <= size[rootY]) {
                parent[rootX] = rootY;
                size[rootY] += size[rootX];
            } else {
                parent[rootY] = rootX;
                size[rootX] += size[rootY];
            }
            count --;
        }
        
        public int count() { return count; }
    }
    
    public int solve(ArrayList<ArrayList<Integer>> A) {
        int n = A.size();
        UF stones = new UF(n);
        for (int i = 0; i < n; i ++) {
            for (int j = i + 1; j < n; j ++) {
                ArrayList<Integer> stone1 = A.get(i);
                ArrayList<Integer> stone2 = A.get(j);
                if (stone1.get(0) == stone2.get(0) || stone1.get(1) == stone2.get(1)) {
                    stones.union(i, j);
                }
            }
        }
        int max = 0;
        for (int i = 0; i < n; i ++) {
            if (stones.parent()[i] != i) continue;
            int val = stones.size()[i];
            if (val >= 2) {
                max += val - 1;
            }
        }
        return max;
    }
}

Solution2:

Time: O(n)

public class Solution {
    static class UF {
        private int count;
        private int[] parent;
        private int[] size;
        
        public UF(int n) {
            this.count = n;
            parent = new int[n];
            size = new int[n];
            for (int i = 0; i < n; i ++) {
                parent[i] = i;
                size[i] = 1;
            }
        }
        
        public int find(int x) {
            while (x != parent[x]) {
                parent[x] = parent[parent[x]];
                x = parent[x];
            }
            return x;
        }
        
        public int[] size() { return size; }
        public int[] parent() { return parent; }

        public void union(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);
            if (rootX == rootY) {
                return;
            }
            if (size[rootX] <= size[rootY]) {
                parent[rootX] = rootY;
                size[rootY] += size[rootX];
            } else {
                parent[rootY] = rootX;
                size[rootX] += size[rootY];
            }
            count --;
        }
        
        public int count() { return count; }
    }
    
    public int solve(ArrayList<ArrayList<Integer>> A) {
        int n = A.size();
        UF stones = new UF(20001);
        for (int i = 0; i < n; i ++) {
            ArrayList<Integer> stone = A.get(i);
            stones.union(stone.get(0), stone.get(1) + 10000);
        }
        Set<Integer> remain = new HashSet<>();
        for (int i = 0; i < n; i ++) {
            ArrayList<Integer> stone = A.get(i);
            int val1 = stones.size()[stones.find(stone.get(0))];
            remain.add(stones.find(stone.get(0)));
        }
        return n - remain.size();
    }
}