核心: 1: 根据给定等价关系划分等价类.
![](https://img-blog.csdn.net/20160604174041142?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
2: 用树来实现集合和集合操作.
3:为了便于实现集合操作, 树采用 "
双亲表示法存储
";
确定等价类的算法:(这里可以搜索详细的算法描述)
1:构造单个元素集合;
2:重复读入偶对,判定所属集合,进行合并;
划分等价类的三个集合
操作:
1: 构造只含单个成员的集合.
2:判定某个单元素所在集合.
3:归并两个互不相交的集合为一个集合.
//树的表示. 一个集合就是一棵树;
1
2
3
4
5
6
7
8
|
//树的表示: 双亲表示法
class
ParentTree{
Object data;
//数据
ParentTree parent;
//该节点的父亲
}
|
以此为例来进行说明和程序演示:
假设集合S= {x | 1<= x <=8
} R是 S上的一个等价关系:
R = {(1,2),(3,4),(5,6),(7,8),(1,3),(5,7),(1,5)};
如题: 如果函数div_equalClass() 调用 find_mfset() 和 merge_mfset() 确定等价类,结果生成的树如下:
Java代码实现如下:
import java.util.ArrayList;
//树与等价问题; MFset表示集合,它由n个子集合构成.
//以此为例来进行说明和程序演示:
//假设集合S= {x | 1<= x <=8 } R是 S上的一个等价关系:
//R = {(1,2),(3,4),(5,6),(7,8),(1,3),(5,7),(1,5)};
public class MFset {
//n个子集合,一个集合就是一棵树
private int n;
private ParentTree[] mfSet;
public MFset(Object[] Xi){
this.n = Xi.length;
mfSet = new ParentTree[n+1];
initial(Xi);
}
//初始化操作,构造一个由n个子集Si...Sn(每个子集只含单个成员Xi)构成的集合S。
private void initial(Object[] Xi){
for(int i=0;i<Xi.length;i++){
mfSet[i+1] = new ParentTree(Xi[i]);//构造一个只含单个成员Xi的集合
}
}
//查找函数,确定x所属集合Si
public ParentTree find_mfset(Object x){
ParentTree si = null;
//找到存储x的节点
for(ParentTree ele : mfSet){
if(ele!=null && ele.toString().equals(x.toString())){
si = ele; break;
}
}
if(si == null) return si;//x不存在
while(si.parent != null){
si = si.parent;
}
return si;
}
//查找函数 增强版,确定x所属集合Si,压缩路径:并将从i至根路径上的所有节点都变成根的孩子节点
public ParentTree fix_mfset(Object x){
ParentTree si = null;
//找到存储x的节点
for(ParentTree ele : mfSet){
if(ele!=null && ele.toString().equals(x.toString())){
si = ele; break;
}
}
if(si == null) return si;//x不存在
ArrayList<ParentTree> path = new ArrayList<>();//存储搜索路径
while(si.parent != null){
path.add(si);
si = si.parent;
}
for(ParentTree ele : path)//将从i至根路径上的所有节点都变成根的孩子节点
ele.parent = si;
return si;
}
//合并函数,将子集合Si和Sj中的一个并入另一个中,Si和Sj为子树根节点。
public void merge_mfset(ParentTree Si,ParentTree Sj){
if(Si == null || Sj==null)return;
Sj.parent = Si;
}
//合并函数增强版, 并之前判断子集中所含成员的数目,然后令成员少的子树并入到含成员多的子树
// 因此需要修改存储结构,令根节点的parent域存储子集中所含成员数目的负值。初始为-1
// 另一种方法: 增加一个存储数目的域count
//
public void mix_mfset(ParentTree Si,ParentTree Sj){
//Si和Sj为子树根节点。
if(Si == null || Sj==null)return;
if(Si.count > Sj.count){
Si.count += Sj.count;
Sj.parent = Si;
}
else{
Sj.count += Si.count;
Si.parent = Sj;
}
}
//根据等价关系划分等价类,即 2:重复读入偶对,判定所属集合,进行合并;
public void div_equalClass(NumPair[] numPairs){
for(int i=0;i<numPairs.length;i++){
ParentTree S1 = fix_mfset(numPairs[i].x);//增强版查找函数
ParentTree S2 = fix_mfset(numPairs[i].y);
/*ParentTree S1 = find_mfset(numPairs[i].x);//原版查找函数
ParentTree S2 = find_mfset(numPairs[i].y);*/
if(S1 != S2){
merge_mfset(S1,S2);//合并普通版
//mix_mfset(S1,S2);//合并增强版
}
}
}
public static void main(String[] args){
//演示集合{1,2,3,4,5,6,7,8}
Integer[] S = new Integer[8];
for(int i=0;i<8;i++)
S[i] = new Integer(i+1);
MFset example = new MFset(S);//
//等价关系 R = {(1,2),(3,4),(5,6),(7,8),(1,3),(5,7),(1,5)};
//确定等价类
NumPair[] numPairs = new NumPair[]{new NumPair(1, 2),new NumPair(3, 4),new NumPair(5, 6),new NumPair(7,8)
,new NumPair(1,3),new NumPair(5, 7),new NumPair(1, 5),new NumPair(8,9)};
example.div_equalClass(numPairs);//确定等价类
}
}
//数对
class NumPair{
Object x;
Object y;
public NumPair(Object x,Object y){
this.x = x; this.y = y;
}
}
//树的表示: 双亲表示法
class ParentTree{
Object data ; //数据
int count = -1;//该树的元素数目
ParentTree parent = null; //该节点的父亲
public ParentTree(){};
public ParentTree(Object data){
this.data = data;
}
public String toString(){
return ""+data;
}
}