1. 动态连通性问题
目标:编写一个程序过滤掉序列中无意义的整数对(两个整数均来自同一个等价类中)。
问题描述:当程序从输入中读取了整数对p、q时,如果已知的所有整数对都不能说明p和q是相连的,那么则将这一对整数写入到输出中。如果已知的数据可以说明p和q是相连的,那么程序应该忽略p、q这对整数并继续处理输入中的下一对整数。
应用:网络、变量名等价性、数学集合
网络方面的术语:对象称为触点,整数对成为连接,等价类称为连通分量或是简称分量。
2. API
Union-find算法的API
Pubic class UF |
private int[] id; 分量id private int count; 分量数量 |
public UF(int N) 以整数标识(0—N-1)初始化N个触点 public int count() 连通分量的数量 public boolean connected(int p,int q) 如果p和q存在于同一个分量中则返回true public int find(int p) p(0—N-1)所在分量的标志符 public void union(int p,int q) 在p和q之间添加一条连接 public static void main(String[] args) |
main()方法:
从输入读取N值以及一系列整数对,并对每一对整数调用connected()方法;如果某一对整数中的两个触点已经连通,程序会继续处理下一对数据;如果不连通,程序会调用union()方法并打印这对整数。
用一个以触点为索引的数组id[]作为基本数据结构来表示所有数据分量
3.quick-find算法
package _1_3linkedList;
import java.util.Scanner;
/*算法1.5 union-find 的实现
*/
public class UF
{
private int[] id; /*分量id(以触点为索引)*/
private int count; /*分量数量*/
public UF(int N)
{ /*初始化分量id数组*/
count=N;
id=new int[N]; /*!!!*/
for(int i=0;i<N;i++)
id[i]=i;
}
public int count()
{
return count;
}
public boolean connected(int p,int q)
{
return find(p)==find(q);
}
/******************quick-find算法****************************/
/*
* 使用分量中某个触点的名称作为分量的标志符
* 同一个连通分量中的所有触点在id[]中的值必须全部相同
* 如果最后得到一个连通分量,那么这个算法运行时间是平方级别的
* */
public int find(int p)
{
return id[p];
}
public 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<id.length;i++)/*遍历数组,和p连同的分量要连接到q上去*/
if(id[i]==pId)
id[i]=qId;
count--;
}
public static void main(String[] args)
{
int N;
int p = 0;
int q;
Scanner sc=new Scanner(System.in);
N=sc.nextInt(); /*读取触点数量*/
UF uf=new UF(N); /*初始化N个分量*/
while(true)
{
p=sc.nextInt();
if(p==-1)
break;
q=sc.nextInt(); /*读取整数对*/
if(uf.connected(p, q)) /*如果已连同则忽略*/
continue;
System.out.println(p+" "+q); /*否则打印并归并分量*/
uf.union(p, q);
}
System.out.println(uf.count()+"components");
}
}
4.quick-union算法
/******************quick-union算法****************************/
/*
* 每个触点所对应的id[]元素都是同一个分量中的另一个触点的名称(也可能是他自己的:根触点)
* 当且仅当分别由两个触点开始的这个过程到达了同一个根触点时它们存在于同一个连通分量之中
* 如果最后得到一个连通分量,那么这个算法最佳情况下运行时间是线性级别最坏情况下是平方级别
* */
public int find(int p)
{
while(p!=id[p])
p=id[p];
return p;
}
public void union(int p,int q)
{
int pRoot=find(p);
int qRoot=find(q);
if(pRoot==qRoot)
return;
id[pRoot]=qRoot;
count--;
}
id[pRoot]=qRoot
5.加权quick-union算法
package _1_3linkedList;
import java.util.Scanner;
/*加权quick-union算法
* 添加一个数组和一些代码来记录树中的节点数
* 此算法能保证对数级别的性能
*/
public class WeightedQuickUnionUF
{
private int[] id; /*分量id(以触点为索引)*/
private int count; /*分量数量*/
/**/private int[] sz; /*(由触点索引的)各个根节点所对应的分量的大小*/
public WeightedQuickUnionUF(int N)
{ /*初始化分量id数组*/
count=N;
id=new int[N]; /*!!!*/
/**/ sz=new int[N];
for(int i=0;i<N;i++)
{
id[i]=i;
/**/ sz[i]=1;
}
}
public int count()
{
return count;
}
public boolean connected(int p,int q)
{
return find(p)==find(q);
}
public int find(int p)
{
while(p!=id[p]) /*找到根节点*/
p=id[p];
return p;
}
public void union(int p,int q)
{
int pRoot=find(p);
int qRoot=find(q);
if(pRoot==qRoot)
return;
/**/ if(sz[pRoot]>sz[qRoot])/*将小树的根节点连接到大树的根节点*/
{
id[qRoot]=pRoot;
/**/ sz[qRoot]+=sz[pRoot];
}
else
{
id[pRoot]=qRoot;
/**/ sz[pRoot]+=sz[qRoot];
}
count--;
}
public static void main(String[] args)
{
int N;
int p = 0;
int q;
Scanner sc=new Scanner(System.in);
N=sc.nextInt(); /*读取触点数量*/
WeightedQuickUnionUF wquf=new WeightedQuickUnionUF(N); /*初始化N个分量*/
while(true)
{
p=sc.nextInt();
if(p==-1)
break;
q=sc.nextInt(); /*读取整数对*/
if(wquf.connected(p, q)) /*如果已连同则忽略*/
continue;
System.out.println(p+" "+q); /*否则打印并归并分量*/
wquf.union(p, q);
}
System.out.println(wquf.count()+"components");
}
}