--------------------siwuxie095
Quick Find
这里介绍并查集的一种实现思路:Quick Find
对于一组数据,并查集主要支持两个操作:
(1)union( p , q ),即 并,将 p 和 q 两个元素合并在一起,也就是所谓的连接
(2)find( p ),即 查,查找 p 元素具体在哪个集合中
有了这两个操作,使用并查集也可以轻易地回答这样一个问题:
isConnected( p , q ),传入 p 和 q 两个元素,判断两个元素是否相连接
并查集的基本数据表示
对于如上需求,最简单的数据表示方式就是数组,其中:数组的索引
用来表示元素
如果有 0-9,共 10 个元素,这些元素之间连接关系的表示方法如下:
「即 给每一个元素都赋上一个值」
(1)
0-4 对应的值都是 0,而 5-9 对应的值都是 1。表示 0-4 这 5 个
元素之间是互相连接的,而 5-9 这 5 个元素之间是互相连接的
(2)
0、2、4、6、8 对应的值都是 0,而 1、3、5、7、9 对应的值都
是 1。表示 5 个偶数和 5 个奇数是分别互相连接的
不妨给这个数组起一个名字,叫做 id,即 所有连接在一起的元素,
它们都具有相同的 id
程序:Quick Find 的实现
UnionFind.h:
#ifndef UNIONFIND_H #define UNIONFIND_H
#include <iostream> #include <cassert> using namespace std;
//并查集:Quick Find namespace UF {
class UnionFind {
private: int *id; int count;
public: UnionFind(int count) { this->count = count; id = new int[count]; //在初始情况下,并查集里的元素,两两之间互不连接 for (int i = 0; i < count; i++) { id[i] = i; } }
~UnionFind() { delete []id; }
//找到每一个元素所集合的id:直接访问id相应的值即可 //称这种实现为 Quick Find,也就是Find操作非常快, //只需要使用O(1)的时间复杂度就够了 int find(int p) { assert(p >= 0 && p < count); return id[p]; }
//回答两个元素是否相互连接的问题:id相同则相互连接 bool isConnected(int p, int q) { return find(p) == find(q); }
//Quick Find下的Union操作的时间复杂度 O(n) //(因为union在C++中是关键字,所以不能把函 //数名起成union) void unionElements(int p, int q) {
int pID = find(p); int qID = find(q);
if (pID == qID) { return; }
//Union操作是将两个元素所在集合全部并在一起,而不是只将 p 元素 //并到 q 元素所在集合,或只将 q 元素并到 p 元素所在集合 // //这样,本来两个元素所在集合的所有元素,两两之间就互相连接了 for (int i = 0; i < count; i++) { //或者反向亦可 if (id[i] == pID) { id[i] = qID; } }
} }; }
#endif |
UnionFindTestHelper.h:
#ifndef UNIONFINDTESTHELPER_H #define UNIONFINDTESTHELPER_H
#include "UnionFind.h" #include <iostream> #include <ctime> using namespace std;
namespace UnionFindTestHelper {
void testUF(int n) { //设置随机种子 srand(time(NULL)); UF::UnionFind uf = UF::UnionFind(n);
time_t startTime = clock();
//先进行n次的并,即 Union 操作 for (int i = 0; i < n; i++) { int a = rand() % n; int b = rand() % n; uf.unionElements(a, b); }
//再进行n次的查,即 Find 操作 for (int i = 0; i < n; i++) { int a = rand() % n; int b = rand() % n; uf.isConnected(a, b); }
time_t endTime = clock();
//打印2*n个操作耗费的时间 cout << "UF, " << 2 * n << " ops, " << double(endTime - startTime) / CLOCKS_PER_SEC << " s" << endl; } }
//由于单个Union操作的时间复杂度是O(n)级别的,所以执行n次Union操作, //时间复杂度就是O(n^2)级别的 // //而在查看是否连接isConnected()的复杂度是O(1),执行n个操作,就是O(n) // //不过整体上,此次测试的复杂度是O(n^2)级别的 // //即 Quick Find,它查找(Find)的速度非常快,可是在执行并(Union)这个 //操作的时候,效率却不尽人意
#endif |
main.cpp:
#include "UnionFindTestHelper.h" #include <iostream> using namespace std;
int main() { //规模是十万 int n = 100000;
UnionFindTestHelper::testUF(n);
system("pause"); return 0; } |
运行一览:
【made by siwuxie095】