英雄算法7月17号
2316
class CheckSet {
//小弟->大哥->头头
private:
int arr[100001];
public:
CheckSet() {
for(int i = 0; i < 100001; ++i) {
arr[i] = i;
}
}
int find(int num) {
return arr[num] = (num == arr[num]) ? num : find(arr[num]);//找到num的头头,如果没找到,那就继续找。同时将沿途所有人都指向最终找到的头头,削减大哥的实力。
}
void Union(int num1, int num2) {
int faIdx1 = find(num1);//找到num1的头头
int faIdx2 = find(num2);//找到num2的头头
if(faIdx1 != faIdx2) {//他们不在同一个帮派
arr[faIdx1] = faIdx2;//让num1的头头变成num2的头头,完成叛变
}
}
};
class Solution {
public:
long long countPairs(int n, vector<vector<int>>& edges) {
CheckSet c = CheckSet();//建立并查集
for(auto& x: edges) {//遍历图的所有节点,建立并查集
c.Union(x[0], x[1]);//建立区块结构,连接每一个区块间的内部成员
}
unordered_map<long,long> hash;
for(int i = 0; i < n; ++i) {//找到每一个连通区块的根节点,并且统计每个区块的节点数量
++hash[c.find(i)];
}
long long ans = 0;
for(auto iter = hash.begin(); iter != hash.end(); ++iter) {
ans += (iter->second) * (n - iter->second);//计算结果
}
return ans/2;
}
};
思路
并查集
并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。
举个例子:
我们眼前有一群人,而这群人可以统一用一个数组来表示。而这群人又会拉帮结派,形成不同的团体。每个团体又会有自己的领袖,也就是头头,为了在数组中表示头头,我们只需要让所有的小弟指向大哥,大哥在指向头头;或者所有人都指向头头即可。如果遇到帮派吞并,那么只要让新帮派的所有成员指向其他帮派的头头即可完成吞并。
有了并查集对象(本文是:CheckSet)
- find
- 找到某个人的头头
- 寻找的过程中,将所有人都指向头头,而不是大哥
- Union
- 让两个人指向同一个头头