思路总结: 用并查集来 处理已经相互联通的路段,那么需要添加的路数 是就集合个数减一
举一个栗子:如果 1 2 3 相互连通 4 5 6 相互连通 7 8相互连通 那么只需要 2条 就可以让他么全部联通 比如 3 4连通 4 6连通这样就全部连通了,现在就要构造这么一个并查集。
用一维数组来存放节点,初始化数组使下标值等于数组的值,表示当前节点是一个独立的节点,即一开始所有节点的根节点为他本身
查操作:用来查找当前节点的根节点,初始化后array[1]=1,array[2]=2,array[3]=3 。。。。array[i]==i就表示已经查到了根节点了反之不等那么就查他父节点的上一层,直至找到为止。
并操作: 根据输入的值来做合并,比如 1 2 有连通,那么我们先查找 1 2之前的根节点,如果1 2根节点不同就表示他们没有连通,那么把其中一方查到根节点最为另一方的根节点的父节点,此时 1 2 根节点相同表示他们已经连通,合并完成
这里查的过程需要做下 路径压缩,使得这深度变小,同时分支变多。来提高效率 及每一个节点的上一层就是根节点,不需要逐层查找,
代码如下
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
public class h1232 {
//找根节点 根节点的特征是arr[i]=i;
public static int find(int arr[],int i) {
int z=i;
//只要当前的节点不是根节点 那就往上寻找根节点
while(arr[z]!=z) {
z=arr[z];
}
//找到根节点后 把根节点下面的节点做 “平级处理”
//上级部门变成兄弟部门(父节点变兄弟节点 2333) 可以理解为根节点分支变多 深度变小
int x=i;
int j;
//只要当前的节点不是根节点 那就往上寻找根节点
while(arr[x]!=z) {
//先记录下此节点的父节点
j=arr[x];
//在让当前节点的父节点直接变为根节点
arr[x]=z;
//在原先的父节点上继续操作
x=j;
}
return z;
}
public static void join (int arr[],int x,int y) {
int xz=find(arr,x);
int yz=find(arr,y);
if(xz!=yz) {
arr[xz]=yz;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
while(sc.hasNext()) {
int n=sc.nextInt();
if(n==0)break;
int m=sc.nextInt();
int arr[]=new int[n+1];
sc.nextLine();
for(int i=1;i<arr.length;i++) {
arr[i]=i;
}
while(m-->0) {
int a=sc.nextInt();
int b=sc.nextInt();
join(arr,a,b);
}
//这里需要在做一次的压缩路径处理
for(int i=1;i<=n;i++) {
find(arr,i);
}
//如果1 2 3 连通 4 5连通
//arr 就变 成 1 1 1 4 4 这样 2个集合就出来了 路数为集合数-1
Set<Integer> s=new HashSet<>();
for(int i=1;i<arr.length;i++) {
s.add(arr[i]);
}
System.out.println(s.size()-1);
}
}
}
图解让你更清晰