根據可以移除石頭的規則:如果一塊石頭的 同行或者同列 上有其他石頭存在,那麼就可以移除這塊石頭。可以發現:一定可以把一個連通圖裏的所有頂點根據這個規則刪到只剩下一個頂點。
爲什麼這麼說呢?既然這些頂點在一個連通圖裏,可以通過遍歷的方式(深度優先遍歷或者廣度優先遍歷)遍歷到這個連通圖的所有頂點。那麼就可以按照遍歷的方式 逆向 移除石頭,最後只剩下一塊石頭。所以:最多可以移除的石頭的個數 = 所有石頭的個數 - 連通分量的個數。
因此問題等效於並查集,不難得出以下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();
}
};