在线等价类:
解决问题
-
合并两个类为一个类:
通过union操作实现;
-
判断两个类是否在同一个类中:
借用find操作找到节点的根,再进行判断
首先我们给节点编号1~n作为索引一边处理
基本c++实现
class BCJ
{
private:
int n;
int *parent;
//n 是节点总数;建立一维数组parent,记录某节点的父亲节点(规定根节点的父亲是 0 )
public:
//初始化每个节点的parent为0(表示每个节点初始都是一个集合)
BCJ()
{
int *parent = new int[n+1];
for(int i=1;i<=n;i++)
parent[i]=0;
}
//Union(i,j)操作,是把根为i, j 的两棵树进行合并
void Union(int i, int j)
{
parent[j] = i;
//这里是把根为 j 的子树合并到根为 i 的子树上合并后的根为 i
}
//Find(i)操作,返回节点 i 的 根节点
int Find(int i)
{ //因为parent里存的是每个节点的父亲节点,所以不断向上回溯,
//直到找到根节点<其父亲为0> )
while(parent[i] != 0)
i=parent[i];
return i;
}
} ;
优化
分析Find(i)操作,如果树很高的话,其操作效率下降许多,访问次数是从根到节点i的高度;
so,我们可以使用下述方法对其进行优化:
- 重量规则:在Union操作中,将两棵子树中节点较多的子树的根作为新树的根节点
- 高度规则:在Union操作授内阁,将两棵子树中高度较高的子树作为新树的根节点
路径压缩:缩短元素 i 到达根节点的路径。
方法有三种:
- 紧凑路径发、
- 路径分割法、
- 路径对折。
下面是C++实现
class betterBCJ
{
private:
int n;
int *parent, *root;
//n 是节点总数;建立一维数组parent,记录某节点的父亲节点(规定根节点的父亲是 0 )
public:
//初始化每个节点的parent为0(表示每个节点初始都是一个集合)
betterBCJ()
{
parent = new int[n+1];
root = new int[n+1];
for(int i=1;i<=n;i++)
{
parent[i]=1;
root[i]=true;
}
}
//Union(i,j)操作,是把根为i, j 的两棵树进行合并
void Union(int i, int j)
{ //重量规则
if(parent[i] < parent[j])
{
parent[j] += parent[i];
root[i]=false;
parent[i]=j;
}//把根为 i 的子树合并到根为 j 的子树上
else
{
parent[i]+=parent[j];
root[j]=false;
parent[j]=i;
}//把根为 j 的子树合并到根为 i 的子树上
//这里是把根为 j 的子树合并到根为 i 的子树上合并后的根为 i
}
//Find(i)操作,返回节点 i 的 根节点
int Find(int i)
{ //因为parent里存的是每个节点的父亲节点,所以不断向上回溯,
//直到找到根节点<其父亲为0> )
int j=i;
while( !root[j])
j=parent[j];
int f=i;
while(i != j)
{
f=parent[i];
parent[i]=j;
i=f;
}
return j;
}
} ;