-
城市和城市连在一块,有多少个省?
-
一群人里面自己和自己亲戚连在一块,有多少组亲戚?
-
你俩是不是一个小区的?
-
他俩是不是有一腿
以上所有问题,抽象,概括为分组问题
老并查集了
这里引用一个知乎大佬的并查集讲解,gang的非常好,我就不赘述了。
大家主要了解一下并查集的两种优化方法,路径压缩使得所有节点在被查询过后都直接连在根节点上(这样下次查询的时候就可以O(1)查到他属于哪个组),按深度合并分组可以保证树的深度不会过分增长。
这个题可谓练手并查集的好题目
代码如下
class Solution {
private:
const static int maxn = 200 + 5;
int fa[maxn], depth[maxn];
/**
* 初始化根节点数组,深度数组
*/
void init(){
for(int i = 0; i < maxn; i++){
fa[i] = i;
depth[i] = 1;
}
}
/**
* 找到某个节点的根节点(他属于哪一组?)
*/
int find(int x){
return (x == fa[x]) ? fa[x] : (fa[x] = find(fa[x]));
}
/**
* 合并两个分组
*/
void merge(int a, int b){
int x = find(a), y = find(b);
if(depth[x] > depth[y])fa[y] = x;
if(depth[y] > depth[x])fa[x] = y;
if(depth[x] == depth[y] && x != y){
fa[x] = y;
depth[y] ++;
}
}
public:
int findCircleNum(vector<vector<int>>& isConnected) {
init();
int n = isConnected.size();
for(int i = 0; i < n; i++){
for(int j = i + 1; j < n; j++){
if(i != j && isConnected[i][j]){
merge(i, j);
}
}
}
set<int>ans;
for(int i = 0; i < n; i++)ans.insert(find(i));//set去一下重,查一下有多少个不同的分组就是多少个省了
return ans.size();
}
};