第一眼我竟然没想到并查集,而是想到了dfs…思路还是很简单的
class Solution {
public int removeStones(int[][] stones) {
int n=stones.length;
boolean []visited=new boolean[n];
int count=0;
for(int i=0;i<n;++i){
if(!visited[i]){
count++;
dfs(stones,i,visited);
}
}
return n-count;
}
private void dfs(int[][]stones,int i,boolean []visited){
visited[i]=true;
for(int j=0;j<stones.length;++j){
if(!visited[j]&&(stones[j][0]==stones[i][0]||stones[j][1]==stones[i][1]))
dfs(stones,j,visited);
}
}
}
其实dfs写到一半就知道可以使用并查集了,又写了一个并查集:
class Solution {
int[]ufs;
public int removeStones(int[][] stones) {
int n=stones.length;
ufs=new int[n];
int count=n;
for(int i=0;i<n;++i){
ufs[i]=i;
}
for(int i=0;i<n-1;++i){
for(int j=i+1;j<n;++j){
if(stones[i][0]==stones[j][0]||stones[j][1]==stones[i][1]){
if(find(i)!=find(j)){
ufs[find(i)]=find(j);
count--;
}
}
}
}
return n-count;
}
private int find(int x){
return x==ufs[x]? x:(ufs[x]=find(ufs[x]));
}
}
这两个都通过了但是效率都不太高。
参考了大佬的写法,对并查集做了一些改进。
改进的思想:
之前的想法是把 点 作为集合来看待,时间复杂度为O(n2),改进的想法是把 横坐标或者纵坐标值作为独立的集合来看待,时间复杂度为O(n)。
至于为什么要给x值加上10001,打个比方:试着去考虑点 (1,2)和(2,4),直接以初始坐标值进行计算的话,会出现什么问题?
class Solution {
int[]ufs;
public int removeStones(int[][] stones) {
int n=stones.length;
ufs=new int[20002];
for(int i=0;i<n;++i){
ufs[stones[i][0]+10001]=stones[i][0]+10001;
ufs[stones[i][1]]=stones[i][1];
}
for(int i=0;i<n;++i){
int []cur=stones[i];
if(find(cur[0]+10001)!=find(cur[1])){
ufs[find(cur[1])]=find(cur[0]+10001);
}
}
Set<Integer>set=new HashSet<>();
for(int i=0;i<n;++i){
set.add(find(stones[i][0]+10001));
//set.add(find(stones[i][1]))也行
}
return n-set.size();
}
private int find(int x){
return x==ufs[x]?x:(ufs[x]=find(ufs[x]));
}
}
leetcode这个月关于并查集的题目很多,感觉还是要多练练。并查集还有个优化 按秩合并,我没写过。后面试着写写比较规范的并查集