今天用我个人对并查集的理解,讲一道并查集的经典题目。
题目:
解题思路:
并查集的题目有一个共同的特性,连接在一起的一些数据能够构成一个集合,就像本题中连在一起的城市是一个省份。那么在这道题目中,我们就是要把这样不同的集合都找出来,看有几个集合,那么就有几个省份。
并查集,就是用合并和查找的方式,来维护集合。
首先是并:我们在遍历省份时,如果有直接连接关系,就把其中一个省份绑定为对方的parent。特别的,如果两个省份都有祖先的话,就把他们的祖先绑定为parent-son关系,这样两边所有的城市都建立了关系,因为我们只在意这些省份存在连接,至于谁当父亲没有关系。下面的find函数是用来找到祖先。
void merge(int x,int y){ parent[find(x)] = find(y); }
然后是查:查就是在并的过程中要用到的操作,查的过程中,因为要找到两个省份最早的那个祖先并建立关系,最开始初始化的时候,把祖先初始化为自己,然后通过递归的向上查找,最后查找到最早的祖先。
int find(int x){return parent[x]==x?x:find(parent[x]);}
最后就是维护集合了,我们通过遍历isConnected, 把有相连关系的进行merge,这样最后就把有关系的都连接到了一起。
连接到一起后,我们遍历parent数组,只要某个城市的祖先不是自己,那么说明它就在别人的集合里,这样就是一个独立的省份。
完整代码:
class Solution {
public:
vector<vector<int>>edges;
vector<int>parent;
int find(int x){return parent[x] == x ? x : find(parent[x]);} //查
void merge(int x,int y){ parent[find(x)] = find(y); } //并
int findCircleNum(vector<vector<int>>& isConnected) {
parent.resize(isConnected.size());
for(int i = 0;i < isConnected.size(); i++){
parent[i] = i;//初始化,把祖先绑定为自己
}
for(int i = 0; i < isConnected.size(); i++){
for(int j = i; j < isConnected.size(); j++){
if (isConnected[i][j] == 1)
merge(i, j);//连接有直接相连关系的城市。
}
}
int count = 0;
for(int i = 0;i < isConnected.size(); i++){
if(find(i) == i){
count++;
}
}
return count;
}
};