并查集(一)
前言
这篇文章的目的主要是看到了一道用并查集的题,刚好上网查资料学了一下并查集,然后记录一下。
并查集理论部分
并查集其实就是利用一个数组来完成数据分类,简单点说当我们拿到一堆数据的时候,这些数据可以根据某些的共性分为好几类。举个例子
当我们有如上图所示各组元素的时候,其实可以发现这些元素可以被分为一共两类,其中苹果是一类,梨是一类,表示成如下
假如我们又遇到一个新的元素,然后又可以将新元素分到其中一类中。用来记录这些关系的数组就是并查集。
所以并查集的形式就是利用一个数组记录分类中的父子关系——fa[i]=j,这就代表i的父结点是j,就像红苹果是苹果的一种,所以fa[红苹果]=苹果。并查集就是利用这样一个数组记录所有关系。
涉及到并查集的操作有如下两个:
查询
public static int find(int x){
if(fa[x]==x){
return x;
}else{
return find(fa[x]);
}
}
查询操作用了递归,目的是为了找到根节点,因为就像甜的红苹果的父结点是红苹果,红苹果的父结点又是苹果。
所以最后要判断两个元素是否属于同一类只需要判断根节点是否相同就行。
合并
public static void merge(int x,int y){//x和y是分属于两类
fa[find(x)]=find(y);
}
这个就是把一类的根节点设置为另一类的根节点的父结点。当然我们仔细思考其实可以发现好多问题,比如为什么不把甜的红苹果的根节点直接设置成苹果,这样查询不是更方便,这就涉及到并查集的压缩优化问题,在今天想写的这题上并没有体现(其实是我没有做出优化)。
并查集的例题
查找知识图谱中的实例知识
题目描述
知识图谱是一种结构化的语义网络,用于描述物理世界中的概念及其实例的相关关系。可以把知识图谱看成是一种有向图,图中的点是概念或实例,图中的边是概念及其实例的相关关系。
现定义一种简单的知识图谱
概念:包括父概念及其子概念,通过subClassOf关系关联,父子概念可以有多个层级;实例:仅和概念之间通过instanceOf关系关联: 关系:以三元组的形式表示,三元组是一个以空格为成员间分隔符的字符串。例如"student subClassOf person"表示student是person的子概念,“apple instanceOf fruit"表示apple是概念fruit的实例。给定一个知识图谱,请编写一个方法,可以根据一个概念查找其所有的实例。如果一个概念拥有子概念,那么返回的结果需要包含其所有子概念的实例;如果输入的概念没有实例,则返回字符串"empty”(说明:输出字符串文本不需要包含引号)。
给定的图谱满足以下限制: 1、有向图中不存在环路 2、所有点和关系的定义对大小写敏感
输入描述:输入第1行,表示图谱中关系的数量n,取值范围[1,10000] 从第2行到n+1行,表示图谱中的关系,每一行是一个关系三元组第n+2行,表示待查找的元节点,是关系三元组中存在的点。每行字符的长度不超过100。
输入
输出
代码:
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int n=Integer.valueOf(sc.nextLine()).intValue();
Set<String> set=new HashSet<String>();
Map<String,String> map=new HashMap<String,String>();
ArrayList<String> ans=new ArrayList<String>();
for(int i=0;i<n;i++){
String[] a=sc.nextLine().split(" ");
if(a[1].equals("instanceOf")||a[1].equals("subClassOf")){
if(a[1].equals("subClassOf")){
set.add(a[0]);
}
map.put(a[0],a[2]);
}
}
String target=sc.nextLine();
for(Map.Entry<String,String> entry:map.entrySet()){
String m= entry.getKey();
String t=entry.getValue();
while(map.get(t)!=null&&!t.equals(target)){
t=map.get(t);
}
if(!set.contains(m)&&t.equals(target)){
ans.add(m);
}
}
Collections.sort(ans);
for(String s:ans){
System.out.println(s);
}
}
结语
距离上次发文隔了一个月,希望能提高频率吧,依旧是每文一享 周杰伦的我要夏天https://www.bilibili.com/video/av68661328