首先我们详细的说明以下问题:问题的输入是一列整数对,其中每个整数都表示一个某种类型的对象,一对整数p,q可以理解为“p和q是相连的”。我们假设“相连”是一种等价关系,这也就意味着它具有:
1.自反性:p和p是相连的。
2.对称性:如果p和q是相连的那么q和p也是相连的。
3.传递性:如果p和q是相连的且q和r是相连的,那么p和r也是相连的。
列子:输入的整数表示一个大型计算机网络中的计算机,而整数对则表示网络中的连接。通过输入的整数对找出我们需要在网络中连接的触点。
算法的API
public int count() //返回连通分量的数量
public void union(int p,int q); //连接p和q;
public boolean connected(int p,int q) //判断p和q是否是连通的
public int find(int p) //找出p的连通分量标识,改标识代表p所在的连接通路
第一种实现:我们用一个整形数组来标识每一个触点,如果两个触点是连通的就把他们的分量标记相等。
代码如下:
public class UF{
private int count;
private int[] a;
//初始化,N代表触点总数,数组标识每一个触点,可连通的话值设为相等的,count为分量数,初始为N,连通一次减1
public UF(int N){
a = new int[N];
count = N;
for(int i=0;i<N;i++)
a[i] = i;
}
//返回分量数量
public int count(){
return count;
}
//判断两个分量是否连通
public boolean connected(int p,int q){
return find(p)==find(q);
}
//获取所在分量标识
public int find(int p){
return a[p];
}
//连接两个分量
public void union(int p,intq){
int flagP = find(p);
int flagQ = find(q);
for(int i=0;i<a.leng;i++){
if(a[i] == flagP)
a[i] = flagQ;
}
count--;
}
//测试方法
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
//读入触点数量
int N = scanner.nextInt();
UF uf = new UF(N);
while(scanner.hasNext()) {
int p = scanner.nextInt();
int q = scanner.nextInt();
if(p == q)break;
if(!uf.connected(p,q)) {
uf.union(p,q);
System.out.println(p+"-->"+q);//打印未连通的节点
}
}
System.out.println("分量数量为:" + count);
}
}
这种实现方法每次连接两个触点需要遍历数组两次,比较费时,还有两种改善的方法,一种是通过树结构来实现,一种是通过加权的树来实现。这两种实现下次写,今天就先到这了。