并查集
在算法设计中,将一个集合和另外一个集合合并时,就会用到并查集。假如不用并查集,你可能会用到集合和列表来实现,这样会使代码看起来很复杂,而且执行效率不高,下面用洛谷的题目P3367 并查集.来举例子:
题目描述:
如题,现在有一个并查集,你需要完成合并和查询操作。
输入格式
第一行包含两个整数N、M,表示共有N个元素和M个操作。
接下来M行,每行包含三个整数Zi、Xi、Yi
当Zi=1时,将Xi与Yi所在的集合合并
当Zi=2时,输出Xi与Yi是否在同一集合内,是的话输出Y;否则话输出N
输出格式
如上,对于每一个Zi=2的操作,都有一行输出,每行包含一个大写字母,为Y或者N
代码(用集合和列表实现)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
public class Main {
public static int n, m;
public static boolean flag[];
public static ArrayList<HashSet<Integer>> als = new ArrayList<>();
public static void union(int x, int y) {
if(x==y) return ;
if (!flag[x] && !flag[y]) {
HashSet<Integer> newset = new HashSet<>();
newset.add(x);
newset.add(y);
flag[x] = true;
flag[y] = true;
als.add(newset);
} else {
if (flag[x] && flag[y]) {
int s = 0, e = 0;
for (int i = 0; i < als.size(); i++)
if (als.get(i).contains(x)) {
s = i;
break;
}
for (int i = 0; i < als.size(); i++) {
if (als.get(i).contains(y)) {
e = i;
break;
}
}
if(e==s)
return;
if (e < s) {
Iterator<Integer> set = als.get(s).iterator();
while (set.hasNext())
als.get(e).add(set.next());
als.remove(s);
} else {
Iterator<Integer> set = als.get(e).iterator();
while (set.hasNext())
als.get(s).add(set.next());
als.remove(e);
}
}
else if (flag[x]) {
int i;
for (i = 0; i < als.size(); i++)
if (als.get(i).contains(x))
break;
als.get(i).add(y);
flag[y] = true;
} else {
int i;
for (i = 0; i < als.size(); i++)
if (als.get(i).contains(y))
break;
als.get(i).add(x);
flag[x] = true;
}
}
}
public static boolean inSet(int x, int y) {
for (int i = 0; i < als.size(); i++)
if (als.get(i).contains(x)) {
if(als.get(i).contains(y))
return true;
else
return false;
}
return true;
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String str[] = reader.readLine().split(" ");
n = Integer.parseInt(str[0]);
m = Integer.parseInt(str[1]);
flag = new boolean[n + 1];
int x, y, z;
StringBuffer sb = new StringBuffer();
for (int i = 0; i < m; i++) {
str = reader.readLine().split(" ");
z = Integer.parseInt(str[0]);
x = Integer.parseInt(str[1]);
y = Integer.parseInt(str[2]);
if (z == 1)
union(x, y);
else {
if (x==y||(flag[x] && flag[y] && inSet(x, y)))
sb.append("Y").append("\n");
else
sb.append("N").append("\n");
}
}
System.out.println(sb);
}
}
通过上面的代码,我们会发现特别麻烦,接下来我们引入并查集:这里介绍如何用它实现集合的合并和判断是否在同一个集合:
判断是否在同一个集合:
在这里我们先定义了一个find方法去查找x所在的集合的根节点,思想:保存每个集合的根节点,初始化为为-1(利用小于0来判读是否为根节点,后面的数值用来保存该集合有多少个点),其实就一个树状图,利用x集合保存的时它的父亲节点的父亲的下标,直到出现负数为根节点,再根据根节点判断是否为同一个集合,若根节点相同,则是同一个集合:
代码实现(未压缩的find方法):
public static int find(int x)
{
while(data[x]<0) return x;
return find(data[x]);
}
在这里有一个提高效率的方法,我们可以在找的同时顺带保存根节点,当下次查找的时候直接找到根节点,不需要每次都递归去寻找。
代码实现(压缩的find方法):
public static int find(int x)
{
while(data[x]<0) return x;
return data[x]=find(data[x]);
}
接下来我们就可以利用find函数来实现查找两个数是否属于同一个集合。
work方法:
public static boolean work(int x,int y)
{
int a=find(x),b=find(y);
if(a==b)
return true;
return false;
}
最后我们利用find方法来实现合并的union方法。
union方法:
public static void union(int x,int y)
{
int a=find(x),b=find(y);
if(a==b)
return ;
data[a]+=data[b];
data[b]=a;
}
完整代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static int n,m,z,x,y;
public static int data[];
public static int find(int x)
{
if(data[x]<0) return x;
return data[x]=find(data[x]);
}
public static void union()
{
int a=find(x);
int b=find(y);
if(a==b) return ;
data[a]+=data[b];
data[b]=a;
}
public static void main(String[] args) throws IOException {
BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
String str[]=reader.readLine().split(" ");
n=Integer.parseInt(str[0]); m=Integer.parseInt(str[1]);
data=new int[n+1];
for(int i=1;i<=n;i++)
data[i]=-1;
StringBuffer sb=new StringBuffer();
for(int i=0;i<m;i++)
{
str=reader.readLine().split(" ");
z=Integer.parseInt(str[0]); x=Integer.parseInt(str[1]);
y=Integer.parseInt(str[2]);
if(z==1)
union();
else
{
if(find(x)==find(y))
sb.append("Y").append("\n");
else
sb.append("N").append("\n");
}
}
System.out.println(sb);
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)