union-find算法
动态连通型性:
问题的输入是一列整数对,其中每个整数都表示一个某种类型的对象,一对整数pq可以被理解为“p和q是相连的”。我们假设相连是一种对等关系,则它具有:
自反性
对称性
传递性
对等关系能够将对象分为多个等价类。在这里,当且仅当两个对象相连时它们才属于同一个等价类。我们的目标是编写一个程序来过滤序列中所有无意义的整数对(两个整数均来自同一个等价类中)。
换句话说,当程序从输入中读取了整数对p q时,如果已知的所有整数对都不能说明p和q是相连的,那么将这一对整数写入到输出中。如果已知的数据可以说明p和q是相连的,那么程序应该忽略p q这对整数并继续处理输入中的下一对整数。
为了达到所期望的效果,我们需要设计一个数据结构来保存程序已知的所有整数对的足够多的信息,并用它们来判断一对新对象是否是相连的,我们将这个问题通俗的叫做动态连通性问题。
动态连通性问题在数学集合中的应用:
我们可以将输入的所有整数看做属于不同的数学集合。在处理一个整数对p q时,我们是在判断它们是否属于相同的集合。如果不是,我们会将p所属的集合和q所属的集合进行归并,最终所有的整数属于同一个集合。
下面我们统一术语,将对象称为触点,将整数对称为连接,将等价类称为连通分量。假设我们有用0到N-1的整数所表示的N个触点。这样做并不会降低算法的通用性,因为我们可以将整数标志符和任意名称关联起来。
连通性问题只要求我们的程序能够判别给定的整数对p q是否相连,但是没有要求给出两者之间的通路上的所有连接,这样的要求会使问题更复杂,并使用了另外一对算法。
union-find算法的API
class UnionFind
UnionFind(int N); //以整数标识(0-N-1)初始化N个触点
void union(int p, int q); //在p和q之间添加一条连接
int find(int p); //p所在的分量的标识符(0-N-1)
bool connected(int p, int q); //如果p和q在同一个连通分量中返回true
int count(); //连通分量的数目
如果两个触点在不同的分量中,union()操作会将分量归并,find()操作会返回给定触点所在的连通分量的标识符。connected()操作能够判断两个触点是否存在于同一个分量之中。count()方法会返回所有连通分量的数量。一开始我们有N个分量,将两个分量归并的每次union()操作都会使分量总数减去1。
一开始我们有N个连通分量,每个触点都构成了一个只含有它自己的分量,因此我们将id[i]的值初始化为i。其中i在0-N-1之间,对于每个触点i,我们将find()方法来判断它所在的连通分量所需的信息保存在id[i]之中。connected()方法的实现只有一条语句find(p )==find(q),它返回一个布尔值。
class UnionFind
{
public:
UnionFind();
void Union(int p, int q);
int Find(int p);
bool Connected(int p, int q);
int Count();
void ProcessData(string filePath);
void MainProcess();
void PrintInfo();
private:
vector<int> vertex;
int _count;
vector<pair<int, int>> _connect_data;
queue<pair<int, int>> _que;
};
void UnionFind::PrintInfo()
{
set<int> st;
map<int, vector<int>> mp;
vector<int> vec;
for (int i = 0; i < vertex.size(); i++)
{
st.insert(vertex[i]);
}
int cnt = 1;
for (auto itr = st.begin(); itr != st.end(); itr++)
{
int value = *itr;
for (int i = 0; i < vertex.size(); i++)
{
if (vertex[i] == value)
{
vec.push_back(i);
}
}
mp.insert(pair<int, vector<int>>(cnt, vec));
cnt++;
vec.clear();
}
cout << _count << " components" << endl;
for (auto itr = mp.begin(); itr != mp.end(); itr++)
{
cout << "Index " << itr->first << " component link vertex : ";
for (int i = 0; i < itr->second.size(); i++)
{
cout << itr->second[i] << " ";
}
cout << endl;
}
}
void UnionFind::MainProcess()
{
while (!_que.empty())
{
int node1 = _que.front().first;
int node2 = _que.front().second;
_que.pop();
if (Connected(node1, node2))
{
continue;
}
Union(node1, node2);
}
PrintInfo();
}
void UnionFind::ProcessData(string filePath)
{
ifstream ifs;
ifs.open(filePath.c_str(), ios::in);
if (!ifs.is_open())
{
cout << "open file error" << endl;
}
string line;
int cnt = 1;
while (getline(ifs, line))
{
int node1;
int node2;
stringstream ss(line);
ss >> node1;
ss >> node2;
if (cnt == 1)
{
_count = node1;
cnt = 0;
continue;
}
_connect_data.push_back(pair<int, int>(node1, node2));
_que.push(pair<int, int>(node1, node2));
}
for (int i = 0; i < _count; i++)
{
vertex.push_back(i);
}
return;
}
UnionFind::UnionFind()
{
}
int UnionFind::Find(int p)
{
return vertex[p];
}
int UnionFind::Count()
{
return _count;
}
bool UnionFind::Connected(int p, int q)
{
return Find(p) == Find(q);
}
void UnionFind::Union(int p, int q)
{
int root1 = Find(p);
int root2 = Find(q);
if (root1 == root2)
{
return;
}
for (unsigned int i = 0; i < vertex.size(); i++)
{
if (vertex[i] == root1)
{
vertex[i] = root2;
}
}
_count--;
}
void test()
{
UnionFind UF;
UF.ProcessData("tinyUF.txt");
UF.MainProcess();
}
tinyUF.txt
10
4 3
3 8
6 5
9 4
2 1
8 9
5 0
7 2
6 1
1 0
6 7
未完待遇……