不相交集类 ,可以用来解决等价问题,实现起来简单,可以只使用一个数组。
用一个数组id[N] 记录N个触点,初始化Id[i] =i;
实现动态链接的时候,遍历这个数组,如果p和q的id[p] =id[q] 那么不用管,否则要将pq链接起来,使用uion方法,也就是将p的分量重命名为id[q]。
具体实现如下:
package p2; import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; public class UnionFind { public int [] id; public int count; public UnionFind(int N){ //初始化数组 表示有N个触点 count =N; id = new int[N]; for(int i=0;i<N;i++){ id[i] =i; } } /** * * @param p * @return */ public int find(int p){ return id[p]; } public void union(int p,int q){ int pid=find(p); int qid = find(q); if(pid==qid) return ; //将所有被标为p的分量的 标为Q for(int i=0;i<id.length;i++){ if(id[i] == pid) id[i] =qid; } count--; } /** * 若果pq是联通的就返回true * @param p * @param q * @return */ public boolean connected(int p,int q){ return find(p)==find(q); } /** * 返回连通分量的数目 * @return */ public int count(){ return count; } public static void main(String [] args){ try { Scanner sc = new Scanner(new File("fortest")); int N =sc.nextInt(); //System.out.println(N); UnionFind uf = new UnionFind(N); while(sc.hasNextLine()){ int p = sc.nextInt(); int q =sc.nextInt(); if(uf.find(p)==uf.find(q)) continue; uf.union(p, q); System.out.println("建立连接"+p+"和"+q); } System.out.println(uf.count+"还没有连接上的components"); System.out.println(uf.connected(0, 9)); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
测试集fortest如下:
10 4 3 3 8 6 5 9 4 2 1 8 9 5 0 7 2 6 1 1 0 6 7 7 8
以上内容参考算法 第四版。
这种是最简单的quik-find,但是查找效率不高,需要遍历整个数组。后面会介绍quick-union 森林表示以及加权的quick-union.
顺便写一个quick-find 的应用题。修公路的题目盗用同学的博客题的图。哈哈
用java实现思路就是用unionfind N 个村庄,就是N个触点,从1-N 现在连通的有M条路,用A和B表示,那么如果需要将所有的村庄连通,则就是说这些村庄必须两两相连。那么首先,我们将已知的连通数记录下来,然后再将相连的子块标记,count,最后-1就是需要多修的公路数。利用以上的API,代码如下:
package p2; import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; public class UnionFind { public int [] id; public int count; public UnionFind(int N){ //初始化数组 表示有N个触点 count =N; id = new int[N+1]; for(int i=1;i<=N;i++){ id[i] =i; } } /** * * @param p * @return */ public int find(int p){ return id[p]; } public void union(int p,int q){ int pid=find(p); int qid = find(q); if(pid==qid) return ; //将所有被标为p的分量的 标为Q for(int i=0;i<id.length;i++){ if(id[i] == pid) id[i] =qid; } count--; } /** * 若果pq是联通的就返回true * @param p * @param q * @return */ public boolean connected(int p,int q){ return find(p)==find(q); } /** * 返回连通分量的数目 * @return */ public int count(){ return count; } public static void main(String [] args){ int ans=0; try { Scanner sc = new Scanner(new File("fortest")); int N =sc.nextInt(); int mark [] = new int[N+1]; //System.out.println(N); int M = sc.nextInt(); UnionFind uf = new UnionFind(N); while(sc.hasNextLine()){ int p = sc.nextInt(); int q =sc.nextInt(); //if(uf.find(p)==uf.find(q)) continue; uf.union(p, q); System.out.println("建立连接"+p+"和"+q); } for(int i=1;i<=N;i++){ //标记连通块的节点 mark[uf.find(i)] =1; } for(int i=1;i<=N;i++){ if(mark[i]!=0) ans++; } System.out.println("还需要修"+(ans-1)); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
时间: 2024-10-06 13:34:15