在了解并查集之前我们需要了解一下动态连通性, 假设我们现在有10个点,1,2,3…,10。如果10个点相互独立,那么我们现在有10个连通分量。如果我们将1和2连接起来,那么我们现在就有9个连通分量了。现在假设我们不断地输入两个连通的点,怎么动态的求出这些点的连通分量呢?这就可以用到并查集这种数据结构了。
首先我们需要确定有多少个点,每个点属于哪个连通分量。在我们刚才提的问题中是属于比较简单的情况,我们使用数组就可以搞定了。union[i]表示第i个点属于第几个连通分量。
那么关于union find的代码如下:
#include <iostream>
#include <vector>
#include <utility>
using namespace std;
int unionCnt = 0;
vector<int> union10;
void makeUnion() {
for (int i = 1;i <= 10;i++) {
union10.push_back(i);
}
unionCnt = 10;
}
// 普通解法,时间复杂度是O(n)
int findUnion(int index) {
return union10[index];
}
void connect(int node1, int node2) {
int uId1 = findUnion(node1);
int uId2 = findUnion(node2);
for (int i = 0;i < union10.size();i++) {
if (union10[i] == uId1) {
union10[i] = uId2;
}
}
unionCnt--;
}
int main() {
makeUnion();
connectQuick(1,3);
connectQuick(2,3);
connectQuick(5,8);
cout << "union count is " << unionCnt << endl;
}
在上面代码的connect部分我们把node1所有的连通分量都变成了node2的。这无疑是很耗费时间的。如何改进呢?我们可以将一个连通分量看成是一个树,那么查找这个连通分量时查找根节点就可以了。
代码如下:
//quick union,时间复杂度是O(logn)
int findUnionQuick(int index) {
while(index != union10[index - 1]) {
index = union10[index - 1];
}
return index;
}
void connectQuick(int node1, int node2) {
int uId1 = findUnionQuick(node1);
int uId2 = findUnionQuick(node2);
if (uId1 == uId2) {
return;
}
union10[node1 - 1] = node2;
unionCnt--;
return;
}
ps:以前都是用csdn原来的那个编辑器的,现在用markdown编辑器看起来还挺好看的。