一、并查集:查找元素互相关联的集合
Keep Learning 34days;
合并:合并两个集合
查找:判断两个元素是否在一个集合
1、利用数组 father[]
int father[N]; //表示元素i的最高父亲节点(根节点)
如果father[i] = i,说明i是其集合的 根节点(最高点)
2、开始每一个元素都是自己单独一个集合,令father[i] = i;
for(int i = 0;i <= n;i++)
father[i] = i;
3、查找:由于每个集合只能有一个根节点,所以访问每个元素的根节点要不断寻找,直到father[i] = i;
int findFather(int x)
{
while (x != findFather(x)) //只要当前节点还不是根节点
{
x = father[x]; //元素等于自身父亲节点
}
}
4、 判断两个元素是否在一个集合,只要判断他们两个的根节点是否相同:
bool judge(int a, int b)
{
if (father[a] == father[b]) //如果根节点一样
return true;
else
return false;
5、合并两个元素到一个集合中的操作,比如让元素a和元素b合并到一个集合中,只需将father[ findFather[a] ] = findFather(b)或者反过来也可以:
if(findFather(a) != findFather(b)) //如果两个元素不在一个集合中,合并集合
father[ findFather[a] ] = findFather(b); //更新a的根节点的父亲节点为b的根节点
二、路径压缩:以上并查集没有进行过优化,效率运行低、慢,当你回溯去寻找父亲节点时时间复杂度为O(n),如果有10五次幂显然无法承受
1、举个栗子:fa[1]=1,fa[2]=1,fa[3]=2,fa[4]=3;
这样完全可以等价的变为:fa[1]=1,fa[2]=1,fa[3]=1,fa[4]=1;
相当于把同一集合所有查询路径上的父亲节点都指向根节点,时间复杂度降到了O(1);
int findFather2(int x)
{
int a = x; //由于最后x要变成它的根节点的值,所以先备份它
while (x != findFather(x))
{
x = father[x];
}
while (a != father[a]) //再用x初始值循环一遍
{
int z = a; //由于a查找的过程又会被覆盖,每次用z来备份
a = father[a]; //先将当前节点变为其父亲节点
father[z] = x; //再将刚刚的节点的父亲节点改为根节点的值
}
}