问题描述
在一个二维平面上有若干块石头,每块石头位于某个整数坐标点上。每个坐标点上最多只能有一块石头。如果一块石头的同一行或同一列上存在其他石头,那么可以移除这块石头。
给定一个长度为 n 的数组 stones,其中 stones[i] = [xi, yi] 表示第 i 块石头的坐标。你的任务是计算可以移除的石头的最大数量。
测试样例
样例1:
输入:
stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2],[3,3],[2,3]]
输出:7
样例2:
输入:
stones = [[0,0]]
输出:0
样例3:
输入:
stones = [[0,1],[1,0],[1,1]]
输出:2
解题思路
- 连通分量:我们可以将所有在同一行或同一列上的石头看作一个连通分量。每个连通分量中的石头都可以通过某种顺序移除,直到只剩下一个石头。
- 并查集:为了高效地找到这些连通分量,我们可以使用并查集(Union-Find)数据结构。并查集可以帮助我们快速合并和查找连通分量。
- 计算移除的石头数量:对于每个连通分量,我们可以移除其中的所有石头,除了最后一个。因此,移除的石头数量就是总石头数减去连通分量的数量。
算法步骤
- 初始化并查集:为每个石头创建一个并查集节点。
- 合并连通分量:遍历所有石头,如果两个石头在同一行或同一列,则将它们合并到同一个连通分量中。
- 计算结果:统计连通分量的数量,然后用总石头数减去连通分量的数量,得到可以移除的石头数量。
数据结构选择
- 并查集:用于高效地合并和查找连通分量。
Java代码
import java.util.HashMap;
import java.util.Map;
public class Main {
private int[] parent;
private int count;
public int solution(int[][] stones) {
int n = stones.length;
// 初始化并查集
count = n;
parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
}
// 使用Map来存储行和列的索引
Map<Integer, Integer> rowMap = new HashMap<>();
Map<Integer, Integer> colMap = new HashMap<>();
for (int i = 0; i < n; i++) {
int x = stones[i][0], y = stones[i][1];
// 合并同一行的石头
int finalI = i;
union(i, rowMap.computeIfAbsent(x, k -> finalI));
// 合并同一列的石头
int finalI1 = i;
union(i, colMap.computeIfAbsent(y, k -> finalI1));
}
// 最终的连通分量数量
return n - count;
}
private int find(int p) {
while (p != parent[p]) {
// 路径压缩
parent[p] = parent[parent[p]];
p = parent[p];
}
return p;
}
private void union(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
if (rootP == rootQ) {
return;
}
parent[rootP] = rootQ;
count--; // 减少一个连通分量
}
public static void main(String[] args) {
Main main = new Main();
System.out.println(main.solution(new int[][]{{0, 0}, {0, 1}, {1, 0}, {1, 2}, {2, 1}, {2, 2}, {3, 3}, {2, 3}})); // 输出:7
System.out.println(main.solution(new int[][]{{0, 0}})); // 输出:0
System.out.println(main.solution(new int[][]{{0, 1}, {1, 0}, {1, 1}})); // 输出:2
}
}