用C++实现了一个int类型的并查集,实现了三个版本,是逐个优化的一个过程
第一版
// 第一版(采用一致id的形式,即p,q如果关联,则p, q的id一定完全一致)
class UnionFind_1e {
public:
UnionFind_1e(int n) : count(n) {
id.reserve(count + 1);
for (int i = 0; i <= count; ++i) {
id[i] = i;
}
}
bool isConnected(int p, int q) {
return find(p) == find(q);
}
void unionElements(int p, int q) {
int pid = find(p);
int qid = find(q);
if (pid == qid) {
return;
}
for (int i = 0; i <= count; ++i) {
// 对同一组的数据需要全部逐个赋值,该动作效率较低
if (id[i] == pid) {
id[i] = qid;
}
}
}
private:
int find(int p) {
assert(p >= 0 && p <= count);
// 效率高
return id[p];
}
private:
std::vector<int> id;
int count = 0;
};
第二版
// 第二版(采用parent的方式,即指向如果p,q关联,则值需要保证p, q最终的祖先一致就行)
class UnionFind_2e {
public:
UnionFind_2e(int n) : count(n) {
parent.reserve(count + 1);
for (int i = 0; i <= count; ++i) {
parent[i] = i;
}
}
bool isConnected(int p, int q) {
return find(p) == find(q);
}
void unionElements(int p, int q) {
int proot = find(p);
int qroot = find(q);
if (proot == qroot) {
return;
}
// 效率高
parent[proot] = qroot;
}
private:
int find(int p) {
// 效率低,但是相对1e在unionElements中的逐个赋值效率要高
while (p != parent[p]) {
p = parent[p];
}
return p;
}
private:
std::vector<int> parent;
int count;
};
第二版(层级优化)
// 第二版(按照层级优化,即union的时候,将层级小的一方挂载到层级高的一方)
class UnionFind_2e_rank {
public:
UnionFind_2e_rank(int n) : count(n) {
parent.reserve(count + 1);
for (int i = 0; i <= count; ++i) {
parent[i] = i;
}
rank.resize(count + 1, 1); // 初始每个的层级均为1
}
bool isConnected(int p, int q) {
return find(p) == find(q);
}
void unionElements(int p, int q) {
int proot = find(p);
int qroot = find(q);
if (proot == qroot) {
return;
}
if (rank[proot] < rank[qroot]) {
parent[proot] = qroot;
} else if (rank[proot] > rank[qroot]) {
parent[qroot] = proot;
} else {
// rank[proot] == rank[qroot]
parent[proot] = qroot;
++rank[qroot]; // proot ”挂载“到qroot下面,本来两个层级一致,现在需要增加1
}
}
private:
int find(int p) {
// 效率低,但是相对1e在unionElements中的逐个赋值效率要高
while (p != parent[p]) {
p = parent[p];
}
return p;
}
private:
std::vector<int> parent;
int count;
std::vector<int> rank;
};
第二版(数量优化)
// 第二版(按照数量优化,即union的时候将数量少的一方挂载到数量多的一方)
class UnionFind_2e_sz {
public:
UnionFind_2e_sz(int n) : count(n) {
parent.reserve(count + 1);
for (int i = 0; i <= count; ++i) {
parent[i] = i;
}
sz.resize(count + 1, 1); // 初始每个的数量均为1
}
bool isConnected(int p, int q) {
return find(p) == find(q);
}
void unionElements(int p, int q) {
int proot = find(p);
int qroot = find(q);
if (proot == qroot) {
return;
}
if (sz[proot] < sz[qroot]) {
parent[proot] = qroot;
sz[qroot] += sz[proot]; // 挂载到qroot后,需要将数量累加到qroot上面
} else if (sz[proot] > sz[qroot]) {
parent[qroot] = proot;
sz[proot] += sz[qroot];
} else {
// sz[proot] == sz[qroot]
parent[proot] = qroot;
sz[qroot] += sz[proot];
}
}
private:
int find(int p) {
// 效率低,但是相对1e在unionElements中的逐个赋值效率要高
while (p != parent[p]) {
p = parent[p];
}
return p;
}
private:
std::vector<int> parent;
int count;
std::vector<int> sz;
};
第三版(路径压缩)
// 第三版(防止层级过高带来的查询性能,进行路径压缩优化)
class UnionFind_3e {
public:
UnionFind_3e(int n) : count(n) {
parent.reserve(count + 1);
for (int i = 0; i <= count; ++i) {
parent[i] = i;
}
rank.resize(count + 1, 1); // 初始每个的层级均为1
}
bool isConnected(int p, int q) {
return find(p) == find(q);
}
void unionElements(int p, int q) {
int proot = find(p);
int qroot = find(q);
if (proot == qroot) {
return;
}
if (rank[proot] < rank[qroot]) {
parent[proot] = qroot;
} else if (rank[proot] > rank[qroot]) {
parent[qroot] = proot;
} else {
// rank[proot] == rank[qroot]
parent[proot] = qroot;
++rank[qroot]; // proot ”挂载“到qroot下面,本来两个层级一致,现在需要增加1
}
}
private:
int find(int p) {
// 效率低,但是相对1e在unionElements中的逐个赋值效率要高
// 路径压缩优化实例
// 假设有 4 -> 3 -> 2 -> 1 -> 0的指向,则通过路径压缩优化后变为
// 1 -> 0
// 3 -> 2 -> 0
// 4 -> 2 -> 0
while (p != parent[p]) {
parent[p] = parent[parent[p]]; // 路径压缩优化,请细品
p = parent[p];
}
return p;
}
private:
std::vector<int> parent;
int count;
std::vector<int> rank;
};
测试
void test() {
UnionFind_3e uf(9);
uf.unionElements(0, 1);
uf.unionElements(1, 2);
uf.unionElements(7, 2);
uf.unionElements(5, 2);
uf.unionElements(5, 6);
uf.unionElements(8, 3);
uf.unionElements(3, 4);
uf.unionElements(4, 9);
assert(uf.isConnected(1, 6));
assert(uf.isConnected(7, 6));
assert(uf.isConnected(6, 0));
assert(uf.isConnected(5, 0));
assert(uf.isConnected(5, 7));
assert(!uf.isConnected(8, 7));
assert(!uf.isConnected(0, 9));
assert(uf.isConnected(8, 4));
assert(uf.isConnected(3, 9));
return;
}