根据可以移除石头的规则:如果一块石头的 同行或者同列 上有其他石头存在,那么就可以移除这块石头。可以发现:一定可以把一个连通图里的所有顶点根据这个规则删到只剩下一个顶点。
为什么这么说呢?既然这些顶点在一个连通图里,可以通过遍历的方式(深度优先遍历或者广度优先遍历)遍历到这个连通图的所有顶点。那么就可以按照遍历的方式 逆向 移除石头,最后只剩下一块石头。所以:最多可以移除的石头的个数 = 所有石头的个数 - 连通分量的个数。
因此问题等效于并查集,不难得出以下O(n^2)的代码
class Union{
private:
vector<int> p;
int n;
public:
int find(int x){
if(p[x]!=x) p[x] = find(p[x]);
return p[x];
}
Union(){}
Union(int n){
this->n = n;
p.resize(n);
for(int i=0;i<n;i++) p[i] = i;
}
void unite(int a, int b){
p[find(b)] = find(a);
}
bool isUnite(int a, int b){
return find(a)==find(b);
}
int count(){
int res = 0;
for(int i=0;i<n;i++){
if(p[i]==i){
res++;
}
}
return res;
}
};
class Solution {
public:
int removeStones(vector<vector<int>>& stones) {
int n = stones.size();
int res = 0;
Union un(n);
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(stones[i][0]==stones[j][0]||stones[i][1]==stones[j][1]){
if(!un.isUnite(i,j)){
un.unite(i,j);
}
}
}
}
return n-un.count();
}
};
这里的时间复杂度是O(n^2), 如何进一步优化时间复杂度,答案
将行和列转化到同一个维度(也就是说将行和列仅仅当作一个数字就行)
当我们遍历到一个点(x, y)时,直接将x与y进行合并(说明该行和该列行的所有点都属于同一个并查集)
最后用stones的个数减去并查集的个数即可
但是,x和y的值可能冲突,所以这里我们将x加上10001(题目范围限定为10000)
此时要注意两点
1. 并查集维护的有效根节点的个数变得不确定了,因为维护的是行相等或者列相等的抽象信息,而不在是根节点。
2.所以最后再找根节点的个数时,要借助一个hashmap完成。
class Union{
private:
vector<int> p;
int n;
public:
int find(int x){
if(p[x]!=x) p[x] = find(p[x]);
return p[x];
}
Union(){}
Union(int n){
this->n = n;
p.resize(n);
for(int i=0;i<n;i++) p[i] = i;
}
void unite(int a, int b){
p[find(b)] = find(a);
}
bool isUnite(int a, int b){
return find(a)==find(b);
}
};
const int N = 20010;
class Solution {
public:
int removeStones(vector<vector<int>>& stones) {
int n = stones.size();
Union un(N);
for(auto stone:stones){
un.unite(stone[0],stone[1]+10001);
}
unordered_set<int> res;
for(auto stone:stones){
res.insert(un.find(stone[0]));
}
return n-res.size();
}
};