相关知识:
并查集是一种树型的数据结构,用于处理一些不交集(Disjoint Sets)的合并及查询问题。有一个联合-查找算法(Union-find Algorithm)定义了两个用于此数据结构的操作:
Find:确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。
Union:将两个子集合并成同一个集合。
数据结构定义:
class DisjointSetUnion {
private:
//f是存储父元素,rank是为了是使构建出并查集的树形结构平衡,防止链表
vector<int> f, rank;
int n;
public:
//初始化
DisjointSetUnion(int _n) {
n = _n;
rank.resize(n, 1);
f.resize(n);
for (int i = 0; i < n; i++) {
f[i] = i;
}
}
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
void unionSet(int x, int y) {
int fx = find(x), fy = find(y);
if (fx == fy) {
return;
}
if (rank[fx] < rank[fy]) {
swap(fx, fy);
}
rank[fx] += rank[fy];
f[fy] = fx;
}
};
例题:
思路:
对于source数组中两个任意的位置i,j,如果i,j 在同一个联通分支里,那么i,j 之间就是可以交换的。于是,需要首先遍历 allowedSwaps 数组中所有元素,从而构建source 数组中位置之间的联通关系。然后为每个联通分支 k 维护source 中对应位置元素的集合,以及target 中对应位置元素的集合。随后,汉明距离的最小值,就是这两个集合之间不同的元素的数量。最后依次遍历每一个位置元素是否相同,得出结果。
class DisjointSetUnion {
private:
vector<int> f, rank;
int n;
public:
DisjointSetUnion(int _n) {
n = _n;
rank.resize(n, 1);
f.resize(n);
for (int i = 0; i < n; i++) {
f[i] = i;
}
}
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
void unionSet(int x, int y) {
int fx = find(x), fy = find(y);
if (fx == fy) {
return;
}
if (rank[fx] < rank[fy]) {
swap(fx, fy);
}
rank[fx] += rank[fy];
f[fy] = fx;
}
};
class Solution {
public:
int minimumHammingDistance(vector<int>& source, vector<int>& target, vector<vector<int>>& allowedSwaps) {
int n=source.size();
DisjointSetUnion dsu(n);
for(const auto& e:allowedSwaps){
dsu.unionSet(e[0],e[1]);
}
unordered_map<int,unordered_multiset<int>>s,t;
for(int i=0;i<n;i++){
int fa=dsu.find(i);
s[fa].insert(source[i]);
t[fa].insert(target[i]);
}
int res=0;
for(int i=0;i<n;i++){
if(s.find(i)==s.end()) continue;
for(int x:s[i]){
if(t[i].find(x)==t[i].end()){
res++;
}else{
//每次只删除一个元素
t[i].erase(t[i].find(x));
}
}
}
return res;
}
};