Union-Find算法有它的实际用途。多用于动态连通的应用场景。
Union-Find算法是:给出两个节点,判断它们是否连通,如果连通,是不需要给出具体的路径的
举两个例子作为主要表现:
1、在网路连接中,当发现没有连接的两个节点,可以把他们连接起来,一旦节点都连接起来,又能把多余的线拆除,这时候可以采用Union-Find算法思想;
2、参考编译器在编译一个变量时,额外声明多个指针指向同一变量,这时候,能获知哪些指针实际的指向,也是应用Union-Find思想的时候。
思考路线:
1、首先,构建数组储存编号id [ N ] ,确定一共有多少节点 (N);我们通常选择树的结构储存像这样的数据,树的结构有利于
2、编写Find函数,Find函数旨在告诉我们,Find(int p)里面的p属于那一个 id[P];
3、编写Union函数,Union函数作用是连结两个节点Union(int a,int b)而通常,两个节点都有属于自己的Group。
4、之后我们开始优化,Find函数和Union函数;
5、Find函数,参考(Quick-Union 算法),让Find 函数增加判断,让所寻找的值等于根节点 id[ p ],因为在树的连接时,id号变更了 ;参考(路径压缩的Weighted Quick-Union算法)我们把它进行改造让它实现find时,返回的值与树的根部接近且为1;
6、Union函数,有两步改造参考(Quick-Union 算法)在两个节点联合的时候,把两棵树的其中一棵变为另外一棵树的子树;第二步,参考(Weighted Quick-Union算法),增加两棵树大小的判断,引入树的大小参量。
下面是代码:
#include <stdio.h>
class UF
{
public:
int *id; // access to component id (site indexed)
int count; // number of components
//最原始的Quick-Find
UF(int N)
{
// Initialize component id array.
count = N;
id = new int[N];
for (int i = 0; i < N; i++)
id[i] = i;
}
int Count()
{
return count;
}
bool connected(int p, int q)
{
return find(p) == find(q);
}
int find(int p)
{
return id[p];
}
void Union(int p, int q)
{
// 获得p和q的组号
int pID = find(p);
int qID = find(q);
// 如果两个组号相等,直接返回
if (pID == qID) return;
// 遍历一次,改变组号使他们属于一个组
for (int i = 0; i < (sizeof(id)/sizeof(int)); i++)
if (id[i] == pID) id[i] = qID;
count--;
}//不好的地方是需要全部遍历
//下面是各种改良
//Quick-Union
int find(int p)
{
// 寻找p节点所在组的根节点,根节点具有性质id[root] = root
while (p != id[p]) p = id[p];
return p;
}
void Union(int p, int q)
{
// Give p and q the same root.
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot)
return;
id[pRoot] = qRoot; // 将一颗树(即一个组)变成另外一课树(即一个组)的子树
count--;
}
//Weighted Quick-Union
int *sz;//增加SZ作为树的大小
UF(int N)
{
// Initialize component id array.
count = N;
id = new int[N];
for (int i = 0; i < N; i++)
id[i] = i;
for (int i = 0; i < N; i++)
sz[i] = 1; // 初始情况下,每个组的大小都是1
}
void Union(int p, int q)
{
int i = find(p);
int j = find(q);
if (i == j) return;
// 将小树作为大树的子树
if (sz[i] < sz[j])
{
id[i] = j; sz[j] += sz[i];
}
else
{
id[j] = i; sz[i] += sz[j];
}
count--;
}
//路径压缩的Weighted Quick-Union算法,让树更扁平化
int find(int p)
{
while (p != id[p])
{
// 将p节点的父节点设置为它的爷爷节点
id[p] = id[id[p]];
p = id[p];
}
return p;
}
};