动态连通性问题:
输入一列整数对,其中每个整数都表示一个某种类型的对象,一对整数p,q可以被理解为 是相连的,即p和q属于同一个等价类。
编写一个程序来过滤掉序列中所有无意义的整数对(两个整数均来自于同一个等价类中)。换句话说,当程序从输入中读取了整数对p q时,如果已知p和q是属于同一个等价类则忽略,如果已知的所有整数对都不能说明p和q是相连的,那么则将这一对整数输出。
一:设计API
- UF(int N):以整数标识(0到N-1)连通分量,初始化N个触点
- void union(int p, int q):在p和q之间添加一条连接
- int find(int p):P所在的分量的标识符(0到N-1)
- boolean connected(int p, int q):如果p和q存在于同一个分量中则返回true
- int count():连通分量的数量
二:设计数据结构,确定实例变量
- 用整数表示N个结点
- 用以结点为索引的数组id[]表示结点的分量。一开始每个结点都只含有它自己,初始化 id[i] = i (0<i<N-1)
- 用分量中的最后一个结点的名称作为分量的标识符
- 用一个数组,表示结点的权数,也就是结点的分量大小
三:设计算法
1:判断两个结点是否属于同一连通分量:
- 获取结点A和结点B所在连通分量的最后一个结点
- 如果两个分量的最后一个结点相同,则证明A和B处于同一个分量
2:连接结点的分量 —— 无权值
- 获取准备连接的结点A所在分量的最后一个结点C
- 将连接的结点B所在下标,作为结点C的当前分量值,即 id[A] = B
- 相当于将A结点的分量与B连接
3:连接结点的分量 —— 带权值
- 获取结点A所在分量的最后一个结点C,结点B所在分量的最后一个结点D
- 判断AC分量的总大小和BD分量的总大小,把小的分量连向大的分量
- 将连接的大结点所在下标,作为小结点的当前分量值
- 更新大结点的分量总大小
实现一:
/**
* 动态连通性问题
*/
public class UnionFound {
//结点个数
private int size;
//记录每个结点的连通分量
private int[] id;
//保存结点,方便判断后连通
private int node;
public UnionFound(int size) {
this.size = size;
id = new int[size];
node = -1;
for (int i = 0; i < id.length; i++) {
id[i] = i;
}
}
//连通两个结点
public void union(int p,int q){
id[node] = q;
if(size>1)
size--;
}
//获得结点所在连通分量的末端
public int find(int p){
while (id[p] != p)
p = id[p];
node = p;
return p;
}
//判断是否连通
public boolean connect(int p,int q){
return find(q) == find(p);
}
//获得结点所在连通分量的数量
public int conunt(){
return size;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
UnionFound uf = new UnionFound(n);
scanner.nextLine();
String s = scanner.nextLine();
while (!s.equals(""))
{
String[] strings = s.split(" ");
int p = Integer.parseInt(strings[0]);
int q = Integer.parseInt(strings[1]);
if(uf.connect(p,q))
{
s = scanner.nextLine();
continue;
}
uf.union(p,q);
System.out.println(p+" "+q);
s = scanner.nextLine();
}
System.out.println("总共有"+uf.conunt()+"个连通分量");
}
}
实现二:
/**
* 动态连通性问题
*/
public class UnionFound {
//结点个数
private int size;
//记录每个结点的连通分量
private int[] id;
//保存两个结点,方便连通
private int nodeA,nodeB;
//记录每个结点的连通分量的大小
private int[] power;
public UnionFound(int size) {
this.size = size;
nodeA = nodeB = -1;
id = new int[size];
for (int i = 0; i < id.length; i++) {
id[i] = i;
}
power = new int[size];
for (int i = 0; i < power.length; i++) {
power[i] = 1;
}
}
//连通两个结点
public void union(int p,int q){
if(power[nodeA] < power[nodeB])
{
id[nodeA] = q;
power[nodeB]+=power[nodeA];
}
else
{
id[nodeB] = p;
power[nodeA]+=power[nodeB];
}
if(size>1)
size--;
}
//获得结点所在连通分量的末端
public int find(int p){
while (id[p] != p)
p = id[p];
return p;
}
//判断是否连通
public boolean connect(int p,int q){
nodeA = find(p);
nodeB = find(q);
return nodeA == nodeB;
}
//获得结点所在连通分量的数量
public int conunt(){
return size;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
UnionFound uf = new UnionFound(n);
scanner.nextLine();
String s = scanner.nextLine();
while (!s.equals(""))
{
String[] strings = s.split(" ");
int p = Integer.parseInt(strings[0]);
int q = Integer.parseInt(strings[1]);
if(uf.connect(p,q))
{
s = scanner.nextLine();
continue;
}
uf.union(p,q);
System.out.println(p+" "+q);
s = scanner.nextLine();
}
System.out.println("总共有"+uf.conunt()+"个连通分量");
}
}